The testcase successfully compiles, but then fails to link because we've optimized away the declaration of the variable. We catch this in potential_constant_expression_1, but this path wasn't calling it.
Fixed on trunk by always calling that function, not just in templates. With that change, I needed to adjust pce1 to not require that a variable be initialized yet, so that we can check it within the initializer. To avoid that causing some missed errors, decl_maybe_constant_var_p now considers the initializer if it is already known. Fixed on 7 branch more simply, by calling p_c_e from cxx_eval_constant_expression. Tested x86_64-pc-linux-gnu, applying to trunk and 7.
commit 46761de0ab74a6983c931c13bfb78c095ae4f651 Author: Jason Merrill <ja...@redhat.com> Date: Sat Jun 17 00:00:21 2017 -0400 PR c++/81073 - constexpr and static var in statement-expression. * typeck2.c (store_init_value): Always call require_potential_constant_expression. * pt.c (convert_nontype_argument): Likewise. * constexpr.c (potential_constant_expression_1): Adjust message. Use decl_maybe_constant_var_p instead of decl_constant_var_p. * decl2.c (decl_maybe_constant_var_p): Consider initializer. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index ae24e40..569a247 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -5212,10 +5212,11 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, if (want_rval && !var_in_maybe_constexpr_fn (t) && !type_dependent_expression_p (t) - && !decl_constant_var_p (t) + && !decl_maybe_constant_var_p (t) && (strict || !CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (t)) - || !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (t)) + || (DECL_INITIAL (t) + && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (t))) && COMPLETE_TYPE_P (TREE_TYPE (t)) && !is_really_empty_class (TREE_TYPE (t))) { @@ -5540,21 +5541,21 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, { if (flags & tf_error) error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared " - "%<static%> in %<constexpr%> function", tmp); + "%<static%> in %<constexpr%> context", tmp); return false; } else if (CP_DECL_THREAD_LOCAL_P (tmp)) { if (flags & tf_error) error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared " - "%<thread_local%> in %<constexpr%> function", tmp); + "%<thread_local%> in %<constexpr%> context", tmp); return false; } else if (!DECL_NONTRIVIALLY_INITIALIZED_P (tmp)) { if (flags & tf_error) error_at (DECL_SOURCE_LOCATION (tmp), "uninitialized " - "variable %qD in %<constexpr%> function", tmp); + "variable %qD in %<constexpr%> context", tmp); return false; } } diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 72239ec..a475146 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -4145,10 +4145,19 @@ decl_maybe_constant_var_p (tree decl) /* A proxy isn't constant. */ return false; if (TREE_CODE (type) == REFERENCE_TYPE) - /* References can be constant. */ + /* References can be constant. */; + else if (CP_TYPE_CONST_NON_VOLATILE_P (type) + && INTEGRAL_OR_ENUMERATION_TYPE_P (type)) + /* And const integers. */; + else + return false; + + if (DECL_INITIAL (decl) + && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)) + /* We know the initializer, and it isn't constant. */ + return false; + else return true; - return (CP_TYPE_CONST_NON_VOLATILE_P (type) - && INTEGRAL_OR_ENUMERATION_TYPE_P (type)); } /* Complain that DECL uses a type with no linkage. In C++98 mode this is diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index e5238ad..69ca929 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -6585,10 +6585,10 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) if (complain & tf_error) { int errs = errorcount, warns = warningcount + werrorcount; - if (processing_template_decl - && !require_potential_constant_expression (expr)) - return NULL_TREE; - expr = cxx_constant_value (expr); + if (!require_potential_constant_expression (expr)) + expr = error_mark_node; + else + expr = cxx_constant_value (expr); if (errorcount > errs || warningcount + werrorcount > warns) inform (loc, "in template argument for type %qT ", type); if (expr == error_mark_node) diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 4623d6d..430ba30 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -821,8 +821,7 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags) || (DECL_IN_AGGR_P (decl) && !DECL_VAR_DECLARED_INLINE_P (decl))) { /* Diagnose a non-constant initializer for constexpr. */ - if (processing_template_decl - && !require_potential_constant_expression (value)) + if (!require_potential_constant_expression (value)) value = error_mark_node; else value = cxx_constant_value (value, decl); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag1.C index 450a0b5..ccb8d81 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag1.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag1.C @@ -16,5 +16,5 @@ constexpr int i = ai.f(); constexpr int b = A<B>().f(); // { dg-error "" } template <class T> -constexpr int f (T t) { return 42; } // { dg-error "parameter" } -constexpr int x = f(B()); // { dg-error "constexpr" } +constexpr int f (T t) { return 42; } +constexpr int x = f(B()); // { dg-error "non-literal" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-trivial2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-trivial2.C index d241114..005f07b 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-trivial2.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-trivial2.C @@ -10,5 +10,5 @@ struct T int main() { - constexpr T t = (T{} = T{}); + constexpr T t = (T{} = T{}); // { dg-error "" "" { target c++11_only } } } diff --git a/gcc/testsuite/g++.dg/ext/stmtexpr19.C b/gcc/testsuite/g++.dg/ext/stmtexpr19.C new file mode 100644 index 0000000..0c19a21 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/stmtexpr19.C @@ -0,0 +1,17 @@ +// PR c++/81073 +// { dg-options "" } +// { dg-do compile { target c++11 } } + +struct test { const int *addr; }; + +const test* setup() +{ + static constexpr test atest = + { + ({ static const int inner = 123; &inner; }) // { dg-error "static" } + }; + + return &atest; +} + +int main(){}
commit c477f412dd8521342ab5a5f702b00177512ad36e Author: Jason Merrill <ja...@redhat.com> Date: Mon Jun 19 12:28:11 2017 -0400 PR c++/81073 - constexpr and static var in statement-expression. * constexpr.c (cxx_eval_constant_expression) [DECL_EXPR]: Check potential_constant_expression. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 244b8fd..a28bd02 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -4021,6 +4021,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, break; case DECL_EXPR: + if (!potential_constant_expression (t)) + { + if (!ctx->quiet) + require_potential_constant_expression (t); + *non_constant_p = true; + break; + } { r = DECL_EXPR_DECL (t); if (AGGREGATE_TYPE_P (TREE_TYPE (r)) diff --git a/gcc/testsuite/g++.dg/ext/stmtexpr19.C b/gcc/testsuite/g++.dg/ext/stmtexpr19.C new file mode 100644 index 0000000..0c19a21 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/stmtexpr19.C @@ -0,0 +1,17 @@ +// PR c++/81073 +// { dg-options "" } +// { dg-do compile { target c++11 } } + +struct test { const int *addr; }; + +const test* setup() +{ + static constexpr test atest = + { + ({ static const int inner = 123; &inner; }) // { dg-error "static" } + }; + + return &atest; +} + +int main(){}