Bug 89741 - [9 Regression] static_assert fires when template not instantiated
Summary: [9 Regression] static_assert fires when template not instantiated
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 9.0
: P1 normal
Target Milestone: 9.0
Assignee: Not yet assigned to anyone
URL:
Keywords: rejects-valid
Depends on:
Blocks:
 
Reported: 2019-03-16 23:00 UTC by Tadeus Prastowo
Modified: 2023-02-18 21:19 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2019-03-18 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tadeus Prastowo 2019-03-16 23:00:22 UTC
Consider the following MWE:
-- 8< ------------------------
template<typename T1, typename T2>
struct Y {
  static constexpr bool value = false;
};

template<>
struct Y<void, void> {
  static constexpr bool value = true;
};

template<int x>
struct X {
  static_assert(Y<void, decltype(x)>::value, "1");
};

int main() {
}
-- 8< ------------------------

Compiling it with GCC 8.3, GCC 7.4, GCC 6.3, GCC 5.5, GCC 4.9.4, Clang 6.0, and Clang 7.0 gives no error (see https://www.godbolt.org/z/t8Dkw7).

Compiling it with GCC 9.0 (trunk) gives an error.
Comment 1 Jakub Jelinek 2019-03-18 09:16:02 UTC
This is rejected since r265789.
Comment 2 Jakub Jelinek 2019-03-18 10:14:25 UTC
It is the instantiation_dependent_r hunk in pt.c that breaks this:
@@ -25361,7 +25486,10 @@
       return NULL_TREE;
 
     case TEMPLATE_PARM_INDEX:
-      return *tp;
+      if (dependent_type_p (TREE_TYPE (*tp)))
+	return *tp;
+      /* We'll check value-dependence separately.  */
+      return NULL_TREE;
 
       /* Handle expressions with type operands.  */
     case SIZEOF_EXPR:
which got later changed to:
    case TEMPLATE_PARM_INDEX:
      if (dependent_type_p (TREE_TYPE (*tp)))
        return *tp;
      if (TEMPLATE_PARM_PARAMETER_PACK (*tp))
        return *tp;
      /* We'll check value-dependence separately.  */
      return NULL_TREE;
If I return *tp; unconditionally, the testcase is accepted again.
This is called from:
#0  instantiation_dependent_r (tp=0x7fffffffb308, walk_subtrees=0x7fffffffb23c) at ../../gcc/cp/pt.c:25945
#1  0x000000000183b7e9 in walk_tree_1 (tp=0x7fffffffb308, func=0xb1e156 <instantiation_dependent_r(tree*, int*, void*)>, data=0x0, 
    pset=0x7fffffffb2a0, 
    lh=0xb8e1ca <cp_walk_subtrees(tree_node**, int*, tree_node* (*)(tree_node**, int*, void*), void*, hash_set<tree_node*, default_hash_traits<tree_node*> >*)>) at ../../gcc/tree.c:12110
#2  0x000000000183cf41 in walk_tree_without_duplicates_1 (tp=0x7fffffffb308, func=0xb1e156 <instantiation_dependent_r(tree*, int*, void*)>, 
    data=0x0, 
    lh=0xb8e1ca <cp_walk_subtrees(tree_node**, int*, tree_node* (*)(tree_node**, int*, void*), void*, hash_set<tree_node*, default_hash_traits<tree_node*> >*)>) at ../../gcc/tree.c:12458
#3  0x0000000000b1e5e6 in instantiation_dependent_uneval_expression_p (expression=<template_parm_index 0x7fffea960210>) at ../../gcc/cp/pt.c:26042
#4  0x0000000000b7584e in finish_decltype_type (expr=<template_parm_index 0x7fffea960210>, id_expression_or_member_access_p=true, complain=3)
    at ../../gcc/cp/semantics.c:9363
#5  0x0000000000a5b330 in cp_parser_decltype (parser=0x7ffff7ffbab0) at ../../gcc/cp/parser.c:14688
and for finish_decltype_type nothing is asking about value-dependence I guess.
Comment 3 Jakub Jelinek 2019-03-18 11:21:15 UTC
This might actually be invalid testcase with no diagnostics required though.
Certainly no instantiations of X can be accepted.
Comment 4 Tadeus Prastowo 2019-03-18 14:36:03 UTC
My use-case is to use the instantiation of `struct X' to fire the static assert.
Comment 5 Jakub Jelinek 2019-03-18 14:48:35 UTC
This needs to be verified by our C++ language lawyers, but if:
"If no valid specialization can be generated for a template definition, and that template is not instantiated, the template definition is ill-formed, no diagnostic required."
rule applies in this case, then what you are trying to do is not valid C++ and you'll need to find some other way, e.g. one where there are instantiations that could be instantiated.  Say use
template<typename T1, typename T2>
struct Y {
  static constexpr bool value = false;
};

template<>
struct Y<void, void> {
  static constexpr bool value = true;
};

template<>
struct Y<void, float> {
  static constexpr bool value = true;
};

template<int x, typename T, T y = 0>
struct X {
  static_assert(Y<void, decltype(y)>::value, "1");
};
or something similar where all instantiations aren't invalid.
Comment 6 Jason Merrill 2019-03-18 14:56:50 UTC
(In reply to Jakub Jelinek from comment #5)
> This needs to be verified by our C++ language lawyers, but if:
> "If no valid specialization can be generated for a template definition, and
> that template is not instantiated, the template definition is ill-formed, no
> diagnostic required."
> rule applies in this case, then what you are trying to do is not valid C++

Right.  G++ is giving a correct, though optional, diagnostic; if you want to delay this diagnostic until instantiation, you need to make the expression somehow dependent on template arguments.
Comment 7 Tadeus Prastowo 2019-03-18 15:07:45 UTC
The code in question, which is simplified below to match the real use-case, does not involve template specialization, and so, the quoted passage does not apply:
-- 8< -----------
template<int x = 0>
struct X {
  static_assert(sizeof(decltype(x)) == 200, "1");
};
-- 8< -----------

The said code compiles fine with other GCC and Clang versions mentioned in the initial report.

The code above has the merit of not requiring the specification of a redundant/dummy template argument just to fire the static assertion.
Comment 8 Jakub Jelinek 2019-03-18 15:52:41 UTC
There are indeed no explicit specializations (nor partial specializations) but IMHO the above mentioned sentence talks about all specializations, including the implicit specializations.
Just read the two sentences before that, those say:
"Knowing which names are type names allows the syntax of every template definition to be checked. No diagnostic shall be issued for a template definition for which a valid specialization can be generated."
The example at the end of that paragraph also shows a couple of cases when issues in a template may be diagnosed even when it is not instantiated and when the diagnostics must be deferred until instantiation.
Comment 9 Jakub Jelinek 2019-03-18 16:12:04 UTC
That is the C++11 wording, e.g. the C++17 wording is:
"a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter"
decltype(x) does not depend on the template parameter, just on its type which is known, similarly if you just used sizeof(int)==200.
See also
https://gcc.gnu.org/gcc-7/porting_to.html#hypothetical-instantiation
https://gcc.gnu.org/gcc-8/porting_to.html#hypothetical-instantiation
Comment 10 Tadeus Prastowo 2019-03-18 16:37:48 UTC
Okay, I see it now.  Thank you very much, Jakub, for your clear explanation.
Comment 11 Jonathan Wakely 2019-03-18 20:04:00 UTC
(In reply to Tadeus Prastowo from comment #7)
> The code in question, which is simplified below to match the real use-case,
> does not involve template specialization, and so, the quoted passage does
> not apply:

As Jakub said, the rule refers to all template specializations, not just partial specializations and explicit specializations.

X is a template, X<0> is a specialization of that template.
Comment 12 GCC Commits 2023-02-18 21:19:23 UTC
The trunk branch has been updated by Jason Merrill <jason@gcc.gnu.org>:

https://gcc.gnu.org/g:9944ca17c0766623bce260684edc614def7ea761

commit r13-6133-g9944ca17c0766623bce260684edc614def7ea761
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Feb 10 16:16:45 2023 -0800

    c++: static_assert (false) in template [DR2518]
    
    For a long time, people have expected to be able to write
    static_assert (false) in a template and only have it diagnosed if the
    template is instantiated, but we (and other implementations) gave an error
    about the uninstantiated template because the standard says that if no valid
    instantiation of the template is possible, the program is ill-formed, no
    diagnostic required, and we try to diagnose IFNDR things when feasible.
    
    At the meeting last week we were looking at CWG2518, which wanted to specify
    that an implementation must not accept a program containing a failing #error
    or static_assert.  We also looked at P2593, which proposed allowing
    static_assert in an uninstantiated template.  We ended up combining these
    two in order to avoid requiring implementations to reject programs with
    static_assert (false) in uninstantiated templates.
    
    The committee accepted this as a DR, so I'm making the change to all
    standard modes.  This behavior was also conformant previously, since no
    diagnostic was required in this case.
    
    We continue to diagnose non-constant or otherwise ill-formed conditions, so
    no changes to existing tests were needed.
    
            DR 2518
            PR c++/52809
            PR c++/53638
            PR c++/87389
            PR c++/89741
            PR c++/92099
            PR c++/104041
            PR c++/104691
    
    gcc/cp/ChangeLog:
    
            * semantics.cc (finish_static_assert): Don't diagnose in
            template context.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/DRs/dr2518.C: New test.