// Copyright (C) 2020-2023 Free Software Foundation, Inc.
// This file is part of GCC.
// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3. If not see
// .
#include "rust-tree.h"
#include "fold-const.h"
#include "stringpool.h"
#include "attribs.h"
#include "escaped_string.h"
namespace Rust {
void
mark_exp_read (tree exp)
{
if (exp == NULL)
return;
switch (TREE_CODE (exp))
{
case VAR_DECL:
gcc_fallthrough ();
case PARM_DECL:
DECL_READ_P (exp) = 1;
break;
case ARRAY_REF:
case COMPONENT_REF:
case MODIFY_EXPR:
case REALPART_EXPR:
case IMAGPART_EXPR:
CASE_CONVERT:
case ADDR_EXPR:
case INDIRECT_REF:
case FLOAT_EXPR:
case NON_DEPENDENT_EXPR:
case VIEW_CONVERT_EXPR:
mark_exp_read (TREE_OPERAND (exp, 0));
break;
case COMPOUND_EXPR:
mark_exp_read (TREE_OPERAND (exp, 1));
break;
case COND_EXPR:
if (TREE_OPERAND (exp, 1))
mark_exp_read (TREE_OPERAND (exp, 1));
if (TREE_OPERAND (exp, 2))
mark_exp_read (TREE_OPERAND (exp, 2));
break;
default:
break;
}
}
tree
convert_from_reference (tree val)
{
if (TREE_TYPE (val) && TYPE_REF_P (TREE_TYPE (val)))
{
tree t = TREE_TYPE (TREE_TYPE (val));
tree ref = build1 (INDIRECT_REF, t, val);
mark_exp_read (val);
TREE_SIDE_EFFECTS (ref)
= (TREE_THIS_VOLATILE (ref) || TREE_SIDE_EFFECTS (val));
val = ref;
}
return val;
}
tree
mark_use (tree expr, bool rvalue_p, bool read_p,
location_t loc /* = UNKNOWN_LOCATION */,
bool reject_builtin /* = true */)
{
#define RECUR(t) mark_use ((t), rvalue_p, read_p, loc, reject_builtin)
if (expr == NULL_TREE || error_operand_p (expr))
return expr;
if (reject_builtin)
return error_mark_node;
if (read_p)
mark_exp_read (expr);
bool recurse_op[3] = {false, false, false};
switch (TREE_CODE (expr))
{
case COMPONENT_REF:
case NON_DEPENDENT_EXPR:
recurse_op[0] = true;
break;
case COMPOUND_EXPR:
recurse_op[1] = true;
break;
case COND_EXPR:
recurse_op[2] = true;
if (TREE_OPERAND (expr, 1))
recurse_op[1] = true;
break;
case INDIRECT_REF:
if (REFERENCE_REF_P (expr))
{
/* Try to look through the reference. */
tree ref = TREE_OPERAND (expr, 0);
tree r = mark_rvalue_use (ref, loc, reject_builtin);
if (r != ref)
expr = convert_from_reference (r);
}
break;
case VIEW_CONVERT_EXPR:
if (location_wrapper_p (expr))
{
loc = EXPR_LOCATION (expr);
tree op = TREE_OPERAND (expr, 0);
tree nop = RECUR (op);
if (nop == error_mark_node)
return error_mark_node;
else if (op == nop)
/* No change. */;
else if (DECL_P (nop) || CONSTANT_CLASS_P (nop))
{
/* Reuse the location wrapper. */
TREE_OPERAND (expr, 0) = nop;
/* If we're replacing a DECL with a constant, we also need to
change the TREE_CODE of the location wrapper. */
if (rvalue_p)
TREE_SET_CODE (expr, NON_LVALUE_EXPR);
}
else
{
/* Drop the location wrapper. */
expr = nop;
protected_set_expr_location (expr, loc);
}
return expr;
}
gcc_fallthrough ();
CASE_CONVERT:
recurse_op[0] = true;
break;
default:
break;
}
for (int i = 0; i < 3; ++i)
if (recurse_op[i])
{
tree op = TREE_OPERAND (expr, i);
op = RECUR (op);
if (op == error_mark_node)
return error_mark_node;
TREE_OPERAND (expr, i) = op;
}
return expr;
#undef RECUR
}
tree
mark_rvalue_use (tree e, location_t loc /* = UNKNOWN_LOCATION */,
bool reject_builtin /* = true */)
{
return mark_use (e, true, true, loc, reject_builtin);
}
tree
mark_lvalue_use (tree expr)
{
return mark_use (expr, false, true, input_location, false);
}
tree
mark_lvalue_use_nonread (tree expr)
{
return mark_use (expr, false, false, input_location, false);
}
tree
mark_discarded_use (tree expr)
{
if (expr == NULL_TREE)
return expr;
STRIP_ANY_LOCATION_WRAPPER (expr);
switch (TREE_CODE (expr))
{
case COND_EXPR:
TREE_OPERAND (expr, 2) = mark_discarded_use (TREE_OPERAND (expr, 2));
gcc_fallthrough ();
case COMPOUND_EXPR:
TREE_OPERAND (expr, 1) = mark_discarded_use (TREE_OPERAND (expr, 1));
return expr;
case COMPONENT_REF:
case ARRAY_REF:
case INDIRECT_REF:
case MEMBER_REF:
break;
default:
if (DECL_P (expr))
break;
else
return expr;
}
return mark_use (expr, true, true, input_location, false);
}
tree
convert_to_void (tree expr, impl_conv_void implicit)
{
location_t loc = expr_loc_or_input_loc (expr);
if (expr == error_mark_node || TREE_TYPE (expr) == error_mark_node)
return error_mark_node;
expr = mark_discarded_use (expr);
if (implicit == ICV_CAST)
/* An explicit cast to void avoids all -Wunused-but-set* warnings. */
mark_exp_read (expr);
if (!TREE_TYPE (expr))
return expr;
if (VOID_TYPE_P (TREE_TYPE (expr)))
return expr;
switch (TREE_CODE (expr))
{
case COND_EXPR: {
/* The two parts of a cond expr might be separate lvalues. */
tree op1 = TREE_OPERAND (expr, 1);
tree op2 = TREE_OPERAND (expr, 2);
bool side_effects
= ((op1 && TREE_SIDE_EFFECTS (op1)) || TREE_SIDE_EFFECTS (op2));
tree new_op1, new_op2;
new_op1 = NULL_TREE;
if (implicit != ICV_CAST && !side_effects)
{
if (op1)
new_op1 = convert_to_void (op1, ICV_SECOND_OF_COND);
new_op2 = convert_to_void (op2, ICV_THIRD_OF_COND);
}
else
{
if (op1)
new_op1 = convert_to_void (op1, ICV_CAST);
new_op2 = convert_to_void (op2, ICV_CAST);
}
expr = build3_loc (loc, COND_EXPR, TREE_TYPE (new_op2),
TREE_OPERAND (expr, 0), new_op1, new_op2);
break;
}
case COMPOUND_EXPR: {
/* The second part of a compound expr contains the value. */
tree op1 = TREE_OPERAND (expr, 1);
tree new_op1;
if (implicit != ICV_CAST
&& !warning_suppressed_p (expr /* What warning? */))
new_op1 = convert_to_void (op1, ICV_RIGHT_OF_COMMA);
else
new_op1 = convert_to_void (op1, ICV_CAST);
if (new_op1 != op1)
{
tree t = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (new_op1),
TREE_OPERAND (expr, 0), new_op1);
expr = t;
}
break;
}
case NON_LVALUE_EXPR:
case NOP_EXPR:
/* These have already decayed to rvalue. */
break;
case CALL_EXPR:
maybe_warn_nodiscard (expr, implicit);
break;
case INDIRECT_REF: {
tree type = TREE_TYPE (expr);
int is_reference = TYPE_REF_P (TREE_TYPE (TREE_OPERAND (expr, 0)));
int is_volatile = TYPE_VOLATILE (type);
int is_complete = COMPLETE_TYPE_P (type);
/* Can't load the value if we don't know the type. */
if (is_volatile && !is_complete)
{
switch (implicit)
{
case ICV_CAST:
warning_at (loc, 0,
"conversion to void will not access "
"object of incomplete type %qT",
type);
break;
case ICV_SECOND_OF_COND:
warning_at (loc, 0,
"indirection will not access object of "
"incomplete type %qT in second operand "
"of conditional expression",
type);
break;
case ICV_THIRD_OF_COND:
warning_at (loc, 0,
"indirection will not access object of "
"incomplete type %qT in third operand "
"of conditional expression",
type);
break;
case ICV_RIGHT_OF_COMMA:
warning_at (loc, 0,
"indirection will not access object of "
"incomplete type %qT in right operand of "
"comma operator",
type);
break;
case ICV_LEFT_OF_COMMA:
warning_at (loc, 0,
"indirection will not access object of "
"incomplete type %qT in left operand of "
"comma operator",
type);
break;
case ICV_STATEMENT:
warning_at (loc, 0,
"indirection will not access object of "
"incomplete type %qT in statement",
type);
break;
case ICV_THIRD_IN_FOR:
warning_at (loc, 0,
"indirection will not access object of "
"incomplete type %qT in for increment "
"expression",
type);
break;
default:
gcc_unreachable ();
}
}
/* Don't load the value if this is an implicit dereference, or if
the type needs to be handled by ctors/dtors. */
else if (is_volatile && is_reference)
{
switch (implicit)
{
case ICV_CAST:
warning_at (loc, 0,
"conversion to void will not access "
"object of type %qT",
type);
break;
case ICV_SECOND_OF_COND:
warning_at (loc, 0,
"implicit dereference will not access "
"object of type %qT in second operand of "
"conditional expression",
type);
break;
case ICV_THIRD_OF_COND:
warning_at (loc, 0,
"implicit dereference will not access "
"object of type %qT in third operand of "
"conditional expression",
type);
break;
case ICV_RIGHT_OF_COMMA:
warning_at (loc, 0,
"implicit dereference will not access "
"object of type %qT in right operand of "
"comma operator",
type);
break;
case ICV_LEFT_OF_COMMA:
warning_at (loc, 0,
"implicit dereference will not access "
"object of type %qT in left operand of comma "
"operator",
type);
break;
case ICV_STATEMENT:
warning_at (loc, 0,
"implicit dereference will not access "
"object of type %qT in statement",
type);
break;
case ICV_THIRD_IN_FOR:
warning_at (loc, 0,
"implicit dereference will not access "
"object of type %qT in for increment expression",
type);
break;
default:
gcc_unreachable ();
}
}
else if (is_volatile && TREE_ADDRESSABLE (type))
{
switch (implicit)
{
case ICV_CAST:
warning_at (loc, 0,
"conversion to void will not access "
"object of non-trivially-copyable type %qT",
type);
break;
case ICV_SECOND_OF_COND:
warning_at (loc, 0,
"indirection will not access object of "
"non-trivially-copyable type %qT in second "
"operand of conditional expression",
type);
break;
case ICV_THIRD_OF_COND:
warning_at (loc, 0,
"indirection will not access object of "
"non-trivially-copyable type %qT in third "
"operand of conditional expression",
type);
break;
case ICV_RIGHT_OF_COMMA:
warning_at (loc, 0,
"indirection will not access object of "
"non-trivially-copyable type %qT in right "
"operand of comma operator",
type);
break;
case ICV_LEFT_OF_COMMA:
warning_at (loc, 0,
"indirection will not access object of "
"non-trivially-copyable type %qT in left "
"operand of comma operator",
type);
break;
case ICV_STATEMENT:
warning_at (loc, 0,
"indirection will not access object of "
"non-trivially-copyable type %qT in statement",
type);
break;
case ICV_THIRD_IN_FOR:
warning_at (loc, 0,
"indirection will not access object of "
"non-trivially-copyable type %qT in for "
"increment expression",
type);
break;
default:
gcc_unreachable ();
}
}
if (is_reference || !is_volatile || !is_complete
|| TREE_ADDRESSABLE (type))
{
/* Emit a warning (if enabled) when the "effect-less" INDIRECT_REF
operation is stripped off. Note that we don't warn about
- an expression with TREE_NO_WARNING set. (For an example of
such expressions, see build_over_call in call.cc.)
- automatic dereferencing of references, since the user cannot
control it. (See also warn_if_unused_value() in c-common.cc.)
*/
if (warn_unused_value && implicit != ICV_CAST
&& !warning_suppressed_p (expr, OPT_Wunused_value)
&& !is_reference)
warning_at (loc, OPT_Wunused_value, "value computed is not used");
expr = TREE_OPERAND (expr, 0);
if (TREE_CODE (expr) == CALL_EXPR)
maybe_warn_nodiscard (expr, implicit);
}
break;
}
case VAR_DECL: {
/* External variables might be incomplete. */
tree type = TREE_TYPE (expr);
int is_complete = COMPLETE_TYPE_P (type);
if (TYPE_VOLATILE (type) && !is_complete)
switch (implicit)
{
case ICV_CAST:
warning_at (loc, 0,
"conversion to void will not access "
"object %qE of incomplete type %qT",
expr, type);
break;
case ICV_SECOND_OF_COND:
warning_at (loc, 0,
"variable %qE of incomplete type %qT will "
"not be accessed in second operand of "
"conditional expression",
expr, type);
break;
case ICV_THIRD_OF_COND:
warning_at (loc, 0,
"variable %qE of incomplete type %qT will "
"not be accessed in third operand of "
"conditional expression",
expr, type);
break;
case ICV_RIGHT_OF_COMMA:
warning_at (loc, 0,
"variable %qE of incomplete type %qT will "
"not be accessed in right operand of comma operator",
expr, type);
break;
case ICV_LEFT_OF_COMMA:
warning_at (loc, 0,
"variable %qE of incomplete type %qT will "
"not be accessed in left operand of comma operator",
expr, type);
break;
case ICV_STATEMENT:
warning_at (loc, 0,
"variable %qE of incomplete type %qT will "
"not be accessed in statement",
expr, type);
break;
case ICV_THIRD_IN_FOR:
warning_at (loc, 0,
"variable %qE of incomplete type %qT will "
"not be accessed in for increment expression",
expr, type);
break;
default:
gcc_unreachable ();
}
break;
}
default:;
}
if (!TREE_SIDE_EFFECTS (expr))
expr = void_node;
return expr;
}
void
maybe_warn_nodiscard (tree expr, impl_conv_void implicit)
{
tree call = expr;
if (TREE_CODE (expr) == TARGET_EXPR)
call = TARGET_EXPR_INITIAL (expr);
location_t loc = expr_loc_or_input_loc (call);
tree callee = CALL_EXPR_FN (call);
if (!callee)
return;
tree type = TREE_TYPE (callee);
if (INDIRECT_TYPE_P (type))
type = TREE_TYPE (type);
tree rettype = TREE_TYPE (type);
tree fn = get_fndecl_from_callee (callee);
tree attr;
if (implicit != ICV_CAST && fn
&& (attr = lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn))))
{
escaped_string msg;
tree args = TREE_VALUE (attr);
if (args)
msg.escape (TREE_STRING_POINTER (TREE_VALUE (args)));
const char *format
= (msg ? G_ ("ignoring return value of %qD, that must be used: %<%s%>")
: G_ ("ignoring return value of %qD, that must be used"));
const char *raw_msg = msg ? (const char *) msg : "";
auto_diagnostic_group d;
if (warning_at (loc, OPT_Wunused_result, format, fn, raw_msg))
inform (DECL_SOURCE_LOCATION (fn), "declared here");
}
else if (implicit != ICV_CAST
&& (attr
= lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype))))
{
escaped_string msg;
tree args = TREE_VALUE (attr);
if (args)
msg.escape (TREE_STRING_POINTER (TREE_VALUE (args)));
const char *format
= (msg ? G_ (
"ignoring returned value of type %qT, that must be used: %<%s%>")
: G_ ("ignoring returned value of type %qT, that must be used"));
const char *raw_msg = msg ? (const char *) msg : "";
auto_diagnostic_group d;
if (warning_at (loc, OPT_Wunused_result, format, rettype, raw_msg))
{
if (fn)
inform (DECL_SOURCE_LOCATION (fn), "in call to %qD, declared here",
fn);
inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)),
"%qT declared here", rettype);
}
}
}
location_t
expr_loc_or_loc (const_tree t, location_t or_loc)
{
location_t loc = EXPR_LOCATION (t);
if (loc == UNKNOWN_LOCATION)
loc = or_loc;
return loc;
}
location_t
expr_loc_or_input_loc (const_tree t)
{
return expr_loc_or_loc (t, input_location);
}
// FN is the callee of a CALL_EXPR or AGGR_INIT_EXPR; return the FUNCTION_DECL
// if we can.
tree
get_fndecl_from_callee (tree fn)
{
if (fn == NULL_TREE)
return fn;
if (TREE_CODE (fn) == FUNCTION_DECL)
return fn;
tree type = TREE_TYPE (fn);
if (type == NULL_TREE || !INDIRECT_TYPE_P (type))
return NULL_TREE;
STRIP_NOPS (fn);
if (TREE_CODE (fn) == ADDR_EXPR || TREE_CODE (fn) == FDESC_EXPR)
fn = TREE_OPERAND (fn, 0);
if (TREE_CODE (fn) == FUNCTION_DECL)
return fn;
return NULL_TREE;
}
tree
pointer_offset_expression (tree base_tree, tree index_tree, location_t location)
{
tree element_type_tree = TREE_TYPE (TREE_TYPE (base_tree));
if (base_tree == error_mark_node || TREE_TYPE (base_tree) == error_mark_node
|| index_tree == error_mark_node || element_type_tree == error_mark_node)
return error_mark_node;
tree element_size = TYPE_SIZE_UNIT (element_type_tree);
index_tree = fold_convert_loc (location, sizetype, index_tree);
tree offset
= fold_build2_loc (location, MULT_EXPR, sizetype, index_tree, element_size);
return fold_build2_loc (location, POINTER_PLUS_EXPR, TREE_TYPE (base_tree),
base_tree, offset);
}
// forked from gcc/cp/tree.cc cp_walk_subtrees
/* Apply FUNC to all language-specific sub-trees of TP in a pre-order
traversal. Called from walk_tree. */
tree
rs_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, void *data,
hash_set *pset)
{
enum tree_code code = TREE_CODE (*tp);
tree result;
#define WALK_SUBTREE(NODE) \
do \
{ \
result = rs_walk_tree (&(NODE), func, data, pset); \
if (result) \
goto out; \
} \
while (0)
if (TYPE_P (*tp))
{
/* If *WALK_SUBTREES_P is 1, we're interested in the syntactic form of
the argument, so don't look through typedefs, but do walk into
template arguments for alias templates (and non-typedefed classes).
If *WALK_SUBTREES_P > 1, we're interested in type identity or
equivalence, so look through typedefs, ignoring template arguments for
alias templates, and walk into template args of classes.
See find_abi_tags_r for an example of setting *WALK_SUBTREES_P to 2
when that's the behavior the walk_tree_fn wants. */
if (*walk_subtrees_p == 1 && typedef_variant_p (*tp))
{
*walk_subtrees_p = 0;
return NULL_TREE;
}
}
/* Not one of the easy cases. We must explicitly go through the
children. */
result = NULL_TREE;
switch (code)
{
case TREE_LIST:
WALK_SUBTREE (TREE_PURPOSE (*tp));
break;
case RECORD_TYPE:
if (TYPE_PTRMEMFUNC_P (*tp))
WALK_SUBTREE (TYPE_PTRMEMFUNC_FN_TYPE_RAW (*tp));
break;
case CONSTRUCTOR:
if (COMPOUND_LITERAL_P (*tp))
WALK_SUBTREE (TREE_TYPE (*tp));
break;
case DECL_EXPR:
/* User variables should be mentioned in BIND_EXPR_VARS
and their initializers and sizes walked when walking
the containing BIND_EXPR. Compiler temporaries are
handled here. And also normal variables in templates,
since do_poplevel doesn't build a BIND_EXPR then. */
if (VAR_P (TREE_OPERAND (*tp, 0))
&& (DECL_ARTIFICIAL (TREE_OPERAND (*tp, 0))
&& !TREE_STATIC (TREE_OPERAND (*tp, 0))))
{
tree decl = TREE_OPERAND (*tp, 0);
WALK_SUBTREE (DECL_INITIAL (decl));
WALK_SUBTREE (DECL_SIZE (decl));
WALK_SUBTREE (DECL_SIZE_UNIT (decl));
}
break;
default:
return NULL_TREE;
}
/* We didn't find what we were looking for. */
out:
return result;
#undef WALK_SUBTREE
}
// forked from gcc/cp/tree.cc cp_expr_location
/* Like EXPR_LOCATION, but also handle some tcc_exceptional that have
locations. */
location_t
rs_expr_location (const_tree t_)
{
tree t = CONST_CAST_TREE (t_);
if (t == NULL_TREE)
return UNKNOWN_LOCATION;
return EXPR_LOCATION (t);
}
// forked from gcc/cp/class.cc is_really_empty_class
/* Returns true if TYPE contains no actual data, just various
possible combinations of empty classes. If IGNORE_VPTR is true,
a vptr doesn't prevent the class from being considered empty. Typically
we want to ignore the vptr on assignment, and not on initialization. */
bool
is_really_empty_class (tree type, bool ignore_vptr)
{
if (CLASS_TYPE_P (type))
{
tree field;
tree binfo;
tree base_binfo;
int i;
/* CLASSTYPE_EMPTY_P isn't set properly until the class is actually laid
out, but we'd like to be able to check this before then. */
if (COMPLETE_TYPE_P (type) && is_empty_class (type))
return true;
if (!ignore_vptr && TYPE_CONTAINS_VPTR_P (type))
return false;
for (binfo = TYPE_BINFO (type), i = 0;
BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
if (!is_really_empty_class (BINFO_TYPE (base_binfo), ignore_vptr))
return false;
for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL
&& !DECL_ARTIFICIAL (field)
/* An unnamed bit-field is not a data member. */
&& !DECL_UNNAMED_BIT_FIELD (field)
&& !is_really_empty_class (TREE_TYPE (field), ignore_vptr))
return false;
return true;
}
else if (TREE_CODE (type) == ARRAY_TYPE)
return (integer_zerop (array_type_nelts_top (type))
|| is_really_empty_class (TREE_TYPE (type), ignore_vptr));
return false;
}
// forked from gcc/cp/class.cc is_empty_class
/* Returns 1 if TYPE contains only padding bytes. */
int
is_empty_class (tree type)
{
if (type == error_mark_node)
return 0;
if (!CLASS_TYPE_P (type))
return 0;
return CLASSTYPE_EMPTY_P (type);
}
// forked from gcc/cp/tree.cc array_type_nelts_top
/* Return, as an INTEGER_CST node, the number of elements for TYPE
(which is an ARRAY_TYPE). This counts only elements of the top
array. */
tree
array_type_nelts_top (tree type)
{
return fold_build2_loc (input_location, PLUS_EXPR, sizetype,
array_type_nelts (type), size_one_node);
}
// forked from gcc/cp/tree.cc builtin_valid_in_constant_expr_p
/* Test whether DECL is a builtin that may appear in a
constant-expression. */
bool
builtin_valid_in_constant_expr_p (const_tree decl)
{
STRIP_ANY_LOCATION_WRAPPER (decl);
if (TREE_CODE (decl) != FUNCTION_DECL)
/* Not a function. */
return false;
if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL)
{
if (fndecl_built_in_p (decl, BUILT_IN_FRONTEND))
switch (DECL_FE_FUNCTION_CODE (decl))
{
case RS_BUILT_IN_IS_CONSTANT_EVALUATED:
case RS_BUILT_IN_SOURCE_LOCATION:
case RS_BUILT_IN_IS_CORRESPONDING_MEMBER:
case RS_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
return true;
default:
break;
}
/* Not a built-in. */
return false;
}
switch (DECL_FUNCTION_CODE (decl))
{
/* These always have constant results like the corresponding
macros/symbol. */
case BUILT_IN_FILE:
case BUILT_IN_FUNCTION:
case BUILT_IN_LINE:
/* The following built-ins are valid in constant expressions
when their arguments are. */
case BUILT_IN_ADD_OVERFLOW_P:
case BUILT_IN_SUB_OVERFLOW_P:
case BUILT_IN_MUL_OVERFLOW_P:
/* These have constant results even if their operands are
non-constant. */
case BUILT_IN_CONSTANT_P:
case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
return true;
default:
return false;
}
}
// forked from gcc/cp/decl2.cc decl_maybe_constant_var_p
/* Returns true if DECL could be a symbolic constant variable, depending on
its initializer. */
bool
decl_maybe_constant_var_p (tree decl)
{
tree type = TREE_TYPE (decl);
if (!VAR_P (decl))
return false;
if (DECL_DECLARED_CONSTEXPR_P (decl))
return true;
if (DECL_HAS_VALUE_EXPR_P (decl))
/* A proxy isn't constant. */
return false;
if (TYPE_REF_P (type))
/* References can be constant. */;
else if (RS_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;
}
// forked from gcc/cp/typeck.cc cp_type_quals
/* Returns the type qualifiers for this type, including the qualifiers on the
elements for an array type. */
int
rs_type_quals (const_tree type)
{
int quals;
/* This CONST_CAST is okay because strip_array_types returns its
argument unmodified and we assign it to a const_tree. */
type = strip_array_types (CONST_CAST_TREE (type));
if (type == error_mark_node
/* Quals on a FUNCTION_TYPE are memfn quals. */
|| TREE_CODE (type) == FUNCTION_TYPE)
return TYPE_UNQUALIFIED;
quals = TYPE_QUALS (type);
/* METHOD and REFERENCE_TYPEs should never have quals. */
gcc_assert (
(TREE_CODE (type) != METHOD_TYPE && !TYPE_REF_P (type))
|| ((quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)) == TYPE_UNQUALIFIED));
return quals;
}
} // namespace Rust