Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
This approach does have some downsides, in that this early folding does
potentially impact later-running diagnostic messages and overall could
make things more difficult to understand; I'm not sure what a good
alternative approach would be though. Any thoughts welcome.
I'm also unsure about the OpenMP changes: they're required here because
otherwise it looks like things get confused by the reduction variable
not matching the expressions that end up getting used in the loop body,
but I'm not familiar enough with OpenMP to know if this might have other
ill effects.
-- >8 --
[basic.link] p14.4 says that a declaration naming a TU-local entity is
an exposure, ignoring "any reference to a non-volatile const object or
reference with internal or no linkage initialized with a constant
expression that is not an odr-use".
To implement this, we cannot stream these entities but must fold them
into their underlying values beforehand. This patch does this within
mark_use as a central location, rather than having to reimplement the
ODR-use logic within modules streaming, and doing this multiple times
for e.g. saved constexpr function bodies. This approach also benefits
from all the existing non-modules testcases for the change.
We implement this as an early walk in mark_use. We don't want to just
piggyback off the recursion done here because we need to fold the entire
member access so we don't leave references to TU-local types around,
so the patch adds an early walk to find candidate expressions. To
prevent unnecessarily repeating this the patch also adds a parameter to
indicate when in a recursive call this would be interesting to attempt
again.
This early folding does break a few assumptions elsewhere in the
frontend that need to be adjusted. We need to wrap some more
expressions with location wrappers to not regress diagnostic quality
after folding, and then handle those new location wrappers. We also
need to prevent folding arithmetic on variables with nullptr value so
that constexpr handling can error later, and prevent folding of
dependent static_assertion conditions so that we can keep the original
values around for diagnose_failing_condition. We also need to update
OpenMP handling so that reductions refer to the underlying decl rather
than a reference, in case the reference gets removed later.
This fix leaves out a few cases that are trickier to handle:
- bit fields have special handling and so can't be completely folded
out, as we need to remember in some cases that this was a bit-field
access and a plain INTEGER_CST doesn't have the space for that.
- variables of type pointer-to-member function have a DECL_INITIAL with
a CONSTRUCTOR that has a different type to the containing variable
(which is a typedef-decl). I wasn't able to find a good way to handle
this and I expect it to be a rare case so I'm leaving it for later.
- Templates when building the tree don't call mark_rvalue_use in all
scenarios, and even in places where they do (e.g. binop handling)
they throw away the result and instead call a 'build_min_nt_*'
function with the original operands. This seems complex and
intrusive to fix, so I'll leave that for a later patch as well.
PR c++/119097
PR c++/120005
gcc/c-family/ChangeLog:
* c-common.cc (pointer_int_sum): Add 'fold' parameter to control
whether the expression is constant-folded.
* c-common.h (pointer_int_sum): New parameter.
* c-warn.cc (unwrap_c_maybe_const): Strip location wrappers.
(warn_logical_operator): Likewise.
gcc/cp/ChangeLog:
* call.cc (convert_like_internal): Call mark_{l,r}value_use
instead of the more general mark_use directly.
* constexpr.cc (maybe_warn_about_constant_value): Add missing
auto_diagnostic_group.
* cp-tree.h (disable_early_constant_variable_folding_sentinel):
New RAII sentinel.
(mark_use): Remove declaration.
* expr.cc (disable_early_constant_variable_folding_value): New.
(disable_early_constant_variable_folding_sentinel): Implement.
(fold_no_odr_constant_expr): New function.
(mark_use): Fold rvalue uses of constant variables to their
underlying value. Keep lvalueness of lvalue INDIRECT_REFs.
* parser.cc (cp_parser_postfix_expression): Remove trailing
whitespace.
(omp_maybe_fold_ref_decl): New function.
(cp_parser_omp_var_list_no_open): Get underlying variable for a
constant-initialized reference decl.
* pt.cc (tsubst_omp_clause_decl): Likewise.
(tsubst_stmt): Don't early-fold STATIC_ASSERTs.
(tsubst_expr): Fold away non-ODR usages of references. Support
constant TARGET_EXPRs.
* ptree.cc (cxx_print_xnode): Handle TU_LOCAL_ENTITYs.
* tree.cc (bot_manip): Remember whether a TARGET_EXPR was
constant in break_out_target_exprs.
* typeck.cc (cp_build_binary_op): Wrap the final outputs with
locations in case they lost them.
(cp_pointer_int_sum): Don't fold arithmetic on null pointers.
gcc/testsuite/ChangeLog:
* g++.dg/modules/internal-8_a.C: Remove implemented xfails,
supplement test with new cases for missing bits.
* g++.dg/cpp0x/lambda/lambda-ref3.C: New test.
Signed-off-by: Nathaniel Shead <[email protected]>
---
gcc/c-family/c-common.cc | 17 +-
gcc/c-family/c-common.h | 2 +-
gcc/c-family/c-warn.cc | 2 +
gcc/cp/call.cc | 7 +-
gcc/cp/constexpr.cc | 1 +
gcc/cp/cp-tree.h | 12 +-
gcc/cp/expr.cc | 156 +++++++++++++++---
gcc/cp/parser.cc | 49 +++++-
gcc/cp/pt.cc | 30 +++-
gcc/cp/ptree.cc | 8 +
gcc/cp/tree.cc | 1 +
gcc/cp/typeck.cc | 14 +-
.../g++.dg/cpp0x/lambda/lambda-ref3.C | 14 ++
gcc/testsuite/g++.dg/modules/internal-8_a.C | 59 +++++--
14 files changed, 322 insertions(+), 50 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-ref3.C
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index e7dd4602ac1..6c1b6e383b8 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -3368,11 +3368,12 @@ shorten_compare (location_t loc, tree *op0_ptr, tree
*op1_ptr,
}
/* Return a tree for the sum or difference (RESULTCODE says which)
- of pointer PTROP and integer INTOP. */
+ of pointer PTROP and integer INTOP. If FOLD, attempt to simplify
+ the expression. */
tree
pointer_int_sum (location_t loc, enum tree_code resultcode,
- tree ptrop, tree intop, bool complain)
+ tree ptrop, tree intop, bool fold, bool complain)
{
tree size_exp, ret;
@@ -3469,7 +3470,11 @@ pointer_int_sum (location_t loc, enum tree_code
resultcode,
if (resultcode == MINUS_EXPR)
intop = fold_build1_loc (loc, NEGATE_EXPR, sizetype, intop);
- ret = fold_build_pointer_plus_loc (loc, ptrop, intop);
+ if (fold)
+ ret = fold_build_pointer_plus_loc (loc, ptrop, intop);
+ else
+ ret = build2_loc (loc, POINTER_PLUS_EXPR, TREE_TYPE (ptrop),
+ ptrop, intop);
fold_undefer_and_ignore_overflow_warnings ();
@@ -3499,7 +3504,11 @@ pointer_int_sum (location_t loc, enum tree_code
resultcode,
if (resultcode == MINUS_EXPR)
intop = fold_build1_loc (loc, NEGATE_EXPR, sizetype, intop);
- ret = fold_build_pointer_plus_loc (loc, ptrop, intop);
+ if (fold)
+ ret = fold_build_pointer_plus_loc (loc, ptrop, intop);
+ else
+ ret = build2_loc (loc, POINTER_PLUS_EXPR, TREE_TYPE (ptrop),
+ ptrop, intop);
fold_undefer_and_ignore_overflow_warnings ();
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index b6021d24173..ad2fd402fd0 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -962,7 +962,7 @@ extern tree shorten_compare (location_t, tree *, tree *,
tree *,
enum tree_code *);
extern tree pointer_int_sum (location_t, enum tree_code, tree, tree,
- bool = true);
+ bool = true, bool = true);
/* Add qualifiers to a type, in the fashion for C. */
extern tree c_build_qualified_type (tree, int, tree = NULL_TREE, size_t = 0);
diff --git a/gcc/c-family/c-warn.cc b/gcc/c-family/c-warn.cc
index 09517d28ba1..4b9081223d3 100644
--- a/gcc/c-family/c-warn.cc
+++ b/gcc/c-family/c-warn.cc
@@ -166,6 +166,7 @@ overflow_warning (location_t loc, tree value, tree expr)
static tree
unwrap_c_maybe_const (tree *tp, int *walk_subtrees, void *)
{
+ STRIP_ANY_LOCATION_WRAPPER (*tp);
if (TREE_CODE (*tp) == C_MAYBE_CONST_EXPR)
{
*tp = C_MAYBE_CONST_EXPR_EXPR (*tp);
@@ -239,6 +240,7 @@ warn_logical_operator (location_t location, enum tree_code
code, tree type,
suppress_warning (op_left, OPT_Wlogical_op);
return;
}
+ op_left = stripped_op_left;
/* We do not warn for constants because they are typical of macro
expansions that test for features. */
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 97c8a48c840..c502e791d47 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -8942,9 +8942,10 @@ convert_like_internal (conversion *convs, tree expr,
tree fn, int argnum,
else
gcc_unreachable ();
}
- expr = mark_use (expr, /*rvalue_p=*/!convs->rvaluedness_matches_p,
- /*read_p=*/true, UNKNOWN_LOCATION,
- /*reject_builtin=*/true);
+ if (convs->rvaluedness_matches_p)
+ expr = mark_lvalue_use (expr);
+ else
+ expr = mark_rvalue_use (expr);
if (type_unknown_p (expr))
expr = instantiate_type (totype, expr, complain);
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 6ebe6ebaef6..15b0c5e3f84 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -8657,6 +8657,7 @@ inline_asm_in_constexpr_error (location_t loc, bool
fundef_p)
static void
maybe_warn_about_constant_value (location_t loc, tree decl)
{
+ auto_diagnostic_group d;
static bool explained = false;
if (cxx_dialect >= cxx17
&& warn_interference_size
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6ee945f3ebf..26708e33e6f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7544,10 +7544,16 @@ extern tree generic_targs_for (tree);
extern tree outer_template_args (const_tree);
/* in expr.cc */
+
+/* An RAII sentinel used to restrict early folding of non-ODR uses
+ of constant variables. */
+
+struct disable_early_constant_variable_folding_sentinel {
+ temp_override<bool> ovr;
+ disable_early_constant_variable_folding_sentinel ();
+};
+
extern tree cplus_expand_constant (tree);
-extern tree mark_use (tree expr, bool rvalue_p, bool read_p,
- location_t = UNKNOWN_LOCATION,
- bool reject_builtin = true);
extern tree mark_rvalue_use (tree,
location_t = UNKNOWN_LOCATION,
bool reject_builtin = true);
diff --git a/gcc/cp/expr.cc b/gcc/cp/expr.cc
index 32dc3eee78f..67c7f176dee 100644
--- a/gcc/cp/expr.cc
+++ b/gcc/cp/expr.cc
@@ -86,15 +86,79 @@ cplus_expand_constant (tree cst)
return cst;
}
-/* We've seen an actual use of EXPR. Possibly replace an outer variable
+/* This internal flag controls whether we should perform early folding
+ of non-ODR uses of potentially-constant variables. We generally want
+ to do this so we don't stream non-ODR-uses of TU-local values in
+ modules, but in some cases this needs to be disabled, such as diagnosing
+ a dependent static_assert. */
+
+static bool disable_early_constant_variable_folding_value;
+
+/* The default constructor of disable_early_constant_variable_folding_sentinel
+ sets the above flag. Upon its destruction the previous value of this
+ flag is restored. */
+
+disable_early_constant_variable_folding_sentinel
+::disable_early_constant_variable_folding_sentinel ()
+ : ovr (disable_early_constant_variable_folding_value, true)
+{
+}
+
+/* Folds an EXPR involving a non-ODR used constant VAR to its underlying value.
+ Returns the new value, or NULL_TREE if folding couldn't occur. */
+
+static tree
+fold_no_odr_constant_expr (tree expr, tree var)
+{
+ if (disable_early_constant_variable_folding_value)
+ return NULL_TREE;
+
+ if (!var || !decl_constant_var_p (var))
+ return NULL_TREE;
+
+ /* We need to explicitly check for volatile because some volatile
+ objects (like those of type nullptr_t) might still be valid in
+ constant expressions, but we don't want to fold those. */
+ if (TREE_THIS_VOLATILE (var))
+ return NULL_TREE;
+
+ /* FIXME: Folding PTRMEMFUNCs breaks things because of mismatches
+ between the type of the variable and its CONSTRUCTOR. But some
+ things rely on this mismatch, so let's skip for now. */
+ if (TYPE_PTRMEMFUNC_P (TREE_TYPE (expr)))
+ return NULL_TREE;
+
+ /* We mustn't fold std::hardware_destructive_interference_size
+ here so that maybe_warn_about_constant_value can complain if
+ it's used in a manifestly constant-evaluated context. */
+ if (DECL_CONTEXT (var) == std_node
+ && DECL_NAME (var)
+ && id_equal (DECL_NAME (var), "hardware_destructive_interference_size"))
+ return NULL_TREE;
+
+ /* Don't count a folded use of the value as a use for the purposes
+ of warnings. (Ideally it shouldn't be a DECL_ODR_USE either,
+ but it's too late to put the cat back in the bag...) */
+ bool used = TREE_USED (var);
+ tree t = maybe_constant_value (expr);
+ if (!used)
+ TREE_USED (var) = false;
+ if (TREE_CONSTANT (t))
+ return t;
+
+ return NULL_TREE;
+}
+
+/* We've seen an actual use of EXPR. Possibly replace a variable
reference inside with its constant value or a lambda capture. */
-tree
+static tree
mark_use (tree expr, bool rvalue_p, bool read_p,
- location_t loc /* = UNKNOWN_LOCATION */,
- bool reject_builtin /* = true */)
+ location_t loc = UNKNOWN_LOCATION,
+ bool reject_builtin = true,
+ bool is_first_call = true)
{
-#define RECUR(t) mark_use ((t), rvalue_p, read_p, loc, reject_builtin)
+#define RECUR(t,f) mark_use ((t), rvalue_p, read_p, loc, reject_builtin, (f))
if (expr == NULL_TREE || error_operand_p (expr))
return expr;
@@ -105,11 +169,66 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
if (TREE_TYPE (expr) && VOID_TYPE_P (TREE_TYPE (expr)))
read_p = false;
- if (read_p)
+ if (is_first_call && read_p)
mark_exp_read (expr);
+ /* We want to fold out non-ODR uses of potentially constant variables.
+ Do this first so that we can also correctly fold out member accesses
+ of these variables. */
+ if (is_first_call && rvalue_p
+ && !disable_early_constant_variable_folding_value)
+ {
+ tree var = expr;
+ bool has_indirect_ref = false;
+ while (var && !VAR_P (var))
+ switch (TREE_CODE (var))
+ {
+ case COMPONENT_REF:
+ /* FIXME: We should handle bitfields as well, but we need
+ some way to indicate that the resulting expression is a
+ bit-field value with the given underlying type. For now
+ let's just bail. */
+ if (TREE_CODE (TREE_OPERAND (var, 1)) == FIELD_DECL
+ && DECL_BIT_FIELD (TREE_OPERAND (var, 1)))
+ {
+ var = NULL_TREE;
+ break;
+ }
+ /* FALLTHROUGH */
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case VIEW_CONVERT_EXPR:
+ var = TREE_OPERAND (var, 0);
+ break;
+
+ case INDIRECT_REF:
+ if (REFERENCE_REF_P (var))
+ {
+ has_indirect_ref = true;
+ var = TREE_OPERAND (var, 0);
+ }
+ else
+ var = NULL_TREE;
+ break;
+
+ default:
+ var = NULL_TREE;
+ }
+
+ if (tree t = fold_no_odr_constant_expr (expr, var))
+ {
+ if (has_indirect_ref)
+ t = convert_from_reference (t);
+ return t;
+ }
+ }
+
+ /* Don't perform the above again on recursive calls unless we walk
+ a node that requires it. */
+
tree oexpr = expr;
bool recurse_op[3] = { false, false, false };
+ bool recurse_op_first_call = false;
switch (TREE_CODE (expr))
{
case VAR_DECL:
@@ -121,7 +240,7 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
if (TREE_CODE (TREE_TYPE (cap)) == TREE_CODE (TREE_TYPE (expr))
&& decl_constant_var_p (cap))
{
- tree val = RECUR (cap);
+ tree val = RECUR (cap, true);
if (!is_capture_proxy (val))
{
tree l = current_lambda_expr ();
@@ -133,15 +252,6 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
if (outer_automatic_var_p (expr)
&& decl_constant_var_p (expr))
{
- if (rvalue_p)
- {
- tree t = maybe_constant_value (expr);
- if (TREE_CONSTANT (t))
- {
- expr = t;
- break;
- }
- }
iloc_sentinel l (loc);
expr = process_outer_var_ref (expr, tf_warning_or_error, true);
if (!(TREE_TYPE (oexpr)
@@ -153,9 +263,11 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
recurse_op[0] = true;
break;
case COMPOUND_EXPR:
+ recurse_op_first_call = true;
recurse_op[1] = true;
break;
case COND_EXPR:
+ recurse_op_first_call = true;
recurse_op[2] = true;
if (TREE_OPERAND (expr, 1))
recurse_op[1] = true;
@@ -172,7 +284,7 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
if (!TYPE_REF_P (TREE_TYPE (cap))
&& decl_constant_var_p (cap))
{
- tree val = RECUR (cap);
+ tree val = RECUR (cap, true);
if (!is_capture_proxy (val))
{
tree l = current_lambda_expr ();
@@ -183,7 +295,11 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
}
tree r = mark_rvalue_use (ref, loc, reject_builtin);
if (r != ref)
- expr = convert_from_reference (r);
+ {
+ if (!rvalue_p)
+ TREE_TYPE (r) = cp_build_reference_type (TREE_TYPE (r), false);
+ expr = convert_from_reference (r);
+ }
}
break;
@@ -192,7 +308,7 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
{
loc = EXPR_LOCATION (expr);
tree op = TREE_OPERAND (expr, 0);
- tree nop = RECUR (op);
+ tree nop = RECUR (op, false);
if (nop == error_mark_node)
return error_mark_node;
else if (op == nop)
@@ -249,7 +365,7 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
if (recurse_op[i])
{
tree op = TREE_OPERAND (expr, i);
- op = RECUR (op);
+ op = RECUR (op, recurse_op_first_call);
if (op == error_mark_node)
return error_mark_node;
TREE_OPERAND (expr, i) = op;
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 8d1187f8cda..fbeb8829627 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -8126,7 +8126,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool
address_p, bool cast_p,
error_at (loc, "call to %<__builtin_operator_new%> "
"does not select replaceable global "
"allocation function");
- else
+ else
error_at (loc, "call to %<__builtin_operator_delete%> "
"does not select replaceable global "
"deallocation function");
@@ -39702,6 +39702,32 @@ check_no_duplicate_clause (tree clauses, enum
omp_clause_code code,
error_at (location, "too many %qs clauses", name);
}
+/* DECL is a declaration named in an OMP clause. Returns the underlying
+ contents of the reference if it would be constant-folded by the
+ frontend. */
+
+static tree
+omp_maybe_fold_ref_decl (tree decl)
+{
+ /* This will be handled by tsubst_omp_clause_decl at inst time. */
+ if (processing_template_decl)
+ return decl;
+
+ if (decl_constant_var_p (decl)
+ && TYPE_REF_P (TREE_TYPE (decl)))
+ {
+ tree init = maybe_constant_value (decl);
+ if (TREE_CONSTANT (init))
+ {
+ decl = tree_strip_nop_conversions (init);
+ if (TREE_CODE (decl) == ADDR_EXPR)
+ decl = TREE_OPERAND (decl, 0);
+ }
+ }
+
+ return decl;
+}
+
/* OpenMP 2.5:
variable-list:
identifier
@@ -39816,6 +39842,7 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum
omp_clause_code kind,
if (REFERENCE_REF_P (decl))
decl = TREE_OPERAND (decl, 0);
+ decl = omp_maybe_fold_ref_decl (decl);
for (int i = dims.length () - 1; i >= 0; i--)
decl = grok_omp_array_section (loc, decl, dims[i].low_bound,
dims[i].length);
@@ -39826,13 +39853,16 @@ cp_parser_omp_var_list_no_open (cp_parser *parser,
enum omp_clause_code kind,
/* If we have "*foo" and
- it's an indirection of a reference, "unconvert" it, i.e.
- strip the indirection (to just "foo").
+ strip the indirection (to just "foo"), and maybe fold it
+ to the underlying variable it references.
- it's an indirection of a pointer, turn it into
"foo[0:1]". */
decl = TREE_OPERAND (decl, 0);
STRIP_NOPS (decl);
- if (!ref_p)
+ if (ref_p)
+ decl = omp_maybe_fold_ref_decl (decl);
+ else
decl = grok_omp_array_section (loc, decl, integer_zero_node,
integer_one_node);
}
@@ -39843,11 +39873,16 @@ cp_parser_omp_var_list_no_open (cp_parser *parser,
enum omp_clause_code kind,
decl = TREE_OPERAND (decl, 0);
STRIP_NOPS (decl);
+ decl = omp_maybe_fold_ref_decl (decl);
decl = grok_omp_array_section (loc, decl, idx, integer_one_node);
}
- else if (TREE_CODE (decl) == NON_LVALUE_EXPR
- || CONVERT_EXPR_P (decl))
- decl = TREE_OPERAND (decl, 0);
+ else
+ {
+ if (TREE_CODE (decl) == NON_LVALUE_EXPR
+ || CONVERT_EXPR_P (decl))
+ decl = TREE_OPERAND (decl, 0);
+ decl = omp_maybe_fold_ref_decl (decl);
+ }
goto build_clause;
}
@@ -39908,6 +39943,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum
omp_clause_code kind,
}
if (outer_automatic_var_p (decl))
decl = process_outer_var_ref (decl, tf_warning_or_error);
+ else
+ decl = omp_maybe_fold_ref_decl (decl);
if (decl == error_mark_node)
;
else if (kind != 0) /* kind != OMP_CLAUSE_ERROR */
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 06287f0b045..21c150c5755 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -18121,6 +18121,14 @@ tsubst_omp_clause_decl (tree decl, tree args,
tsubst_flags_t complain,
&& REFERENCE_REF_P (ret)
&& !REFERENCE_REF_P (decl))
ret = TREE_OPERAND (ret, 0);
+ /* Non-ODR usages of references got folded away, let's find the actual
+ variable we're now referring to. */
+ if (decl && VAR_P (decl) && !VAR_P (ret))
+ {
+ STRIP_NOPS (ret);
+ if (TREE_CODE (ret) == ADDR_EXPR)
+ ret = TREE_OPERAND (ret, 0);
+ }
return ret;
}
@@ -19695,6 +19703,11 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t
complain, tree in_decl)
{
tree condition, message;
+ /* We don't want tsubst_expr to fold away type traits (which are
+ typically non-ODR-used constant variables) so that we can helpfully
+ diagnose them in finish_static_assert. */
+ disable_early_constant_variable_folding_sentinel fld;
+
++c_inhibit_evaluation_warnings;
condition = tsubst_expr (STATIC_ASSERT_CONDITION (t), args,
complain, in_decl);
@@ -21997,7 +22010,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t
complain, tree in_decl)
error_at (loc, "call to %<__builtin_operator_new%> "
"does not select replaceable global "
"allocation function");
- else
+ else
error_at (loc, "call to %<__builtin_operator_delete%> "
"does not select replaceable global "
"deallocation function");
@@ -22433,6 +22446,15 @@ tsubst_expr (tree t, tree args, tsubst_flags_t
complain, tree in_decl)
r = wrap;
else if (outer_automatic_var_p (r))
r = process_outer_var_ref (r, complain);
+ else if (!processing_template_decl
+ && decl_constant_var_p (r)
+ && TYPE_REF_P (TREE_TYPE (r)))
+ {
+ /* Fold away any non-ODR used references. */
+ tree init = maybe_constant_value (r);
+ if (TREE_CONSTANT (init))
+ r = init;
+ }
if (!TYPE_REF_P (TREE_TYPE (t)))
/* If the original type was a reference, we'll be wrapped in
@@ -22748,6 +22770,12 @@ tsubst_expr (tree t, tree args, tsubst_flags_t
complain, tree in_decl)
with constant operands. */
RETURN (t);
+ case TARGET_EXPR:
+ /* A TARGET_EXPR will only get here if it's a non-dependent
+ constant expression. */
+ gcc_assert (TREE_CONSTANT (t));
+ RETURN (t);
+
case NON_LVALUE_EXPR:
case VIEW_CONVERT_EXPR:
{
diff --git a/gcc/cp/ptree.cc b/gcc/cp/ptree.cc
index d074e0ea0fd..18c1417b17c 100644
--- a/gcc/cp/ptree.cc
+++ b/gcc/cp/ptree.cc
@@ -410,6 +410,14 @@ cxx_print_xnode (FILE *file, tree node, int indent)
case PTRMEM_CST:
print_node (file, "member", PTRMEM_CST_MEMBER (node), indent+4);
break;
+ case TU_LOCAL_ENTITY:
+ print_node (file, "name", TU_LOCAL_ENTITY_NAME (node), indent+4);
+ if (location_t loc = TU_LOCAL_ENTITY_LOCATION (node))
+ {
+ expanded_location xloc = expand_location (loc);
+ indent_to (file, indent+4);
+ fprintf (file, "%s:%d:%d", xloc.file, xloc.line, xloc.column);
+ }
default:
break;
}
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 03b58ff6005..5e0b6df1160 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -3188,6 +3188,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_)
TARGET_EXPR_LIST_INIT_P (u) = TARGET_EXPR_LIST_INIT_P (t);
TARGET_EXPR_DIRECT_INIT_P (u) = TARGET_EXPR_DIRECT_INIT_P (t);
TARGET_EXPR_ELIDING_P (u) = TARGET_EXPR_ELIDING_P (t);
+ TREE_CONSTANT (u) = TREE_CONSTANT (t);
/* Map the old variable to the new one. */
splay_tree_insert (target_remap,
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index b876409d847..1301616ce34 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -6531,6 +6531,9 @@ cp_build_binary_op (const op_location_t &location,
RESULT_TYPE. */
if (processing_template_decl)
{
+ op0 = maybe_wrap_with_location (op0, cp_expr_location (orig_op0));
+ op1 = maybe_wrap_with_location (op1, cp_expr_location (orig_op1));
+
/* Since the middle-end checks the type when doing a build2, we
need to build the tree in pieces. This built tree will never
get out of the front-end as we replace it when instantiating
@@ -6787,6 +6790,9 @@ cp_build_binary_op (const op_location_t &location,
instrument_expr = instrument_expr1;
}
+ op0 = maybe_wrap_with_location (op0, cp_expr_location (orig_op0));
+ op1 = maybe_wrap_with_location (op1, cp_expr_location (orig_op1));
+
result = build2_loc (location, resultcode, build_type, op0, op1);
if (final_type != 0)
result = cp_convert (final_type, result, complain);
@@ -6900,8 +6906,12 @@ cp_pointer_int_sum (location_t loc, enum tree_code
resultcode, tree ptrop,
pointer_int_sum() anyway. */
complete_type (TREE_TYPE (res_type));
- return pointer_int_sum (loc, resultcode, ptrop,
- intop, complain & tf_warning_or_error);
+ /* Don't fold arithmetic with a null pointer, we need to keep this around
+ so that constexpr evaluation can complain. */
+ bool fold = !integer_zerop (ptrop);
+
+ return pointer_int_sum (loc, resultcode, ptrop, intop,
+ fold, complain & tf_warning_or_error);
}
/* Return a tree for the difference of pointers OP0 and OP1.
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-ref3.C
b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-ref3.C
new file mode 100644
index 00000000000..152d1f7805a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-ref3.C
@@ -0,0 +1,14 @@
+// { dg-do run { target c++11 } }
+
+int x;
+int main() {
+ constexpr int&& r = static_cast<int&&>(x);
+ r = 123;
+
+ // not an ODR-use of r
+ auto l = [] { r = 456; };
+
+ l();
+ if (x != 456)
+ return 1;
+}
diff --git a/gcc/testsuite/g++.dg/modules/internal-8_a.C
b/gcc/testsuite/g++.dg/modules/internal-8_a.C
index 46e07a23cf0..7430a9ebc4e 100644
--- a/gcc/testsuite/g++.dg/modules/internal-8_a.C
+++ b/gcc/testsuite/g++.dg/modules/internal-8_a.C
@@ -1,35 +1,74 @@
-// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" }
+// { dg-additional-options "-fmodules" }
// Non-ODR usages of const variables currently are erroneously
-// reported in templates and constexpr functions; this test
-// XFAILS them until we can implement a fix.
+// reported in templates; this test XFAILS them until we can implement a fix.
export module M;
-namespace { struct internal_t {}; };
+namespace {
+ struct internal_t {};
+ struct A { int x; };
+ struct B { A a; };
+};
static const int value = 123;
static const int& ref = 456;
static const internal_t internal {};
+static constexpr B other { 789 };
+static constexpr const B& other_ref = other;
+static const B& other_ref_ref = (0, other_ref);
constexpr void f(int) {}
-export constexpr
-void no_odr_use_cexpr() { // { dg-bogus "TU-local" "" { xfail *-*-* } }
+constexpr void no_odr_use_cexpr() {
int x = value;
int y = ref;
int z = (internal, 0);
value;
bool b = value < value;
- f(value);
+ f(other_ref_ref.a.x);
}
-export template <typename T>
-void no_odr_use_templ() { // { dg-bogus "TU-local" "" { xfail *-*-* } }
+// FIXME: templates and instantiations
+template <typename T>
+inline void no_odr_use_templ() { // { dg-bogus "TU-local" "" { xfail *-*-* } }
int x = value;
int y = ref;
int z = (internal, 0);
value;
bool b = value < value;
- f(value);
+ f(other_ref_ref.a.x);
}
+template void no_odr_use_templ<int>();
+
+// FIXME: pointers-to-member functions
+struct S {};
+static constexpr int S::* md = nullptr;
+static constexpr void (S::* mfn)() = nullptr;
+constexpr auto test_md() {
+ auto pfn = md;
+ return pfn;
+}
+constexpr auto test_mfn() { // { dg-bogus "TU-local" "" { xfail *-*-* } }
+ auto pfn = mfn;
+ return pfn;
+}
+
+// FIXME: bitfields with internal type
+namespace {
+ struct Bitfield {
+ int x : 5;
+ };
+ constexpr Bitfield bf{ 4 };
+}
+constexpr auto test_bitfield() { // { dg-bogus "TU-local" "" { xfail *-*-* } }
+ return bf.x;
+}
+
+// PR c++/119097
+namespace { static constexpr const int default_val { 0 }; }
+struct A {
+ int value { default_val };
+ inline A() : value(default_val) {}
+ inline static auto a() { return default_val; }
+};
--
2.51.0