Certain contexts require constant expressions. Constant expressions can be evaluated during translation.
constant expression : either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression, or a prvalue core constant expression whose value satisfies the following constraints: [expr.const]p11
core constant expression : an expression E whose evaluation does not evaluate one of the following: see [expr.const]p5
converted constant expression : an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only [expr.const]p10
contextually converted constant expression of type bool
: an expression, contextually converted to bool, where the converted expression is a constant expression and the conversion sequence contains only the conversions above ([expr.const]p10)
manifestly constant-evaluated expression : see [expr.const]p14
potentially constant evaluated expression : see [expr.const]p15
- we require a constant-expression in these contexts:
case efinish_case_label->case_conversion->cxx_constant_value
static_assert(e)finish_static_assert->fold_non_dependent_expr
arr[e]compute_array_index_type_loc->fold_non_dependent_expr
explicit(e)build_explicit_specifier->cxx_constant_value
noexcept(e)build_noexcept_spec->cxx_constant_value
- enumerator
= ebuild_enumerator->cxx_constant_value
alignas(e)cxx_alignas_expr->cxx_constant_value
btfld : e(a member-declaration)check_bitfield_decl->cxx_constant_value
- template-argument
convert_nontype_argument->maybe_constant_value
- to evaluate a constant expression, we use functions like
maybe_constant_valuecxx_constant_valuemaybe_constant_initcxx_constant_initfold_non_dependent_exprfold_non_dependent_init
- the core of constexpr evaluation is
cxx_eval_outermost_constant_expr(more below) - as an aside, other fold functions in the C++ FE:
fold_simple- only few simplifications like
FLOAT_EXPR->REAL_CST, or foldSIZEOF_EXPRs - can call middle-end
const_unop
- only few simplifications like
fold_for_warncp_fully_foldcp_fully_fold_initcp_fold_function
- we now use the pre-generic form for constexpr evaluation
To check if an expression could be constant, you can use:
potential_constant_expressionpotential_rvalue_constant_expressionrequire_potential_constant_expressionrequire_potential_rvalue_constant_expressionrequire_rvalue_constant_expressionis_constant_expressionis_rvalue_constant_expressionrequire_constant_expressionis_static_init_expressionis_nondependent_constant_expressionis_nondependent_static_init_expression
There are various parameters to specify the behavior: if we want to imply an lvalue-rvalue conversion, if an error message should be emitted, whether we want to consider the constexpr context, or if we should be strict (affects VAR_DECLs). For instance, a reinterpreter_cast is not a potential constant expression. Or a temporary of non-literal type is also not a constant expression.
- takes a (non-dependent) constant expression and returns its reduced value
- noop for
CONSTANT_CLASS_Pexpressions - doesn't error on a non-constant expression; is
strict - unlike the similar functions, uses a hash table to store evaluated expressions:
tree -> treemapping incv_cache - if the expression to evaluate isn't already in the hash table, call
cxx_eval_outermost_constant_exprto actually evaluate the expression (see below)
- a constexpr expansion context, just one per evaluate-outermost-constant-expression
- most importantly, contains a hash map of temporaries or local variables within the constant expression:
hash_map<tree,tree> values; - created only in
cxx_eval_outermost_constant_expr, once per each call - now a class; has
hash_set<tree> *modifiable;for[[assume]]
- the constexpr expansion context, we can have more of them per one "main" constexpr expansion context
- we create a new one when evaluating a function call, array reference, a
{}, ... seecxx_eval_builtin_function_call,cxx_eval_call_expression,cxx_eval_array_reference,cxx_eval_bare_aggregate,cxx_eval_vec_init_1 - contains e.g. the innermost call we're evaluating; the constructor we're currently building up (in aggregate initialization); a pointer to its parent; a pointer to the global constexpr context;
quiet/strict/manifestly_const_evalflags
- represent calls to constexpr functions
- contains e.g. the function definition, the mapping of its parameters, and the result of the function call
- the central point to evaluate a constexpr
- create a new
constexpr_global_ctxandconstexpr_ctx - if we're evaluating an aggregate, create a new
{}and put it intoctx.ctor, setctx.object(which is what we're building the CONSTRUCTOR for) - instantiate any constexpr functions named in the expression (P0859, see
instantiate_constexpr_fns) - actually evaluate the expression:
cxx_eval_constant_expression - make sure that we got a constant expression; perform some checks. In constexpr, when an expression couldn't be evaluated, we set
non_constant_p. - unshare the evaluated expression
- return the evaluated expression
- this is where we perform the actual constexpr evaluation
- depends if we want an lvalue or not (for instance, when evaluating
&expwe want an lvalueexp) ctx->strictmakes a difference forVAR_DECLs- depending on the
TREE_CODEof the expression dispatches to more specialized functions, e.g.cxx_eval_loop_expret al - does not handle template codes; those need to be instantiated first via
fold_non_dependent_exprand such. Otherwise you'll get "unexpected expression of kind".
Consider
constexpr int g = 42;
constexpr int foo (int i) {
int r = i * 2;
return r + g;
}
static_assert (foo (4) == 50);finish_static_assertgets as the conditionfoo (NON_LVALUE_EXPR <4>) == 50. Usefold_non_dependent_expr->maybe_constant_valueto evaluate it. We're in amanifestly_const_evalcontext: [expr.const]p14: An expression or conversion is manifestly constant-evaluated if it is: -- a constant-expression, [...] and
static_assert-declaration:
static_assert ( constant-expression ) ;
static_assert ( constant-expression , string-literal ) ;
So we don't look into the cv_cache and go straight to...
cxx_eval_outermost_constant_expr: build up a new global and "local" constexpr context. The expression doesn't contain any constexpr functions to instantiate.- call
cxx_eval_constant_expressionto actually evaluate the expression. We have anEQ_EXPR-> callcxx_eval_binary_expressionon both the LHS and RHS. The RHS is 50, so there's nothing to do. The LHS is aCALL_EXPR, socxx_eval_constant_expressioncalls: cxx_eval_call_expression (foo (NON_LVALUE_EXPR <4>)):-
new
constexpr_call -
lookup the function definition foo in
constexpr_fundef_tableusingretrieve_constexpr_fundef(it is put there byregister_constexpr_fundef):(gdb) p *new_call.fundef $34 = {decl = <function_decl 0x7fffea232e00 foo>, body = <bind_expr 0x7fffea241990>, parms = <parm_decl 0x7fffea242100 i>, result = <result_decl 0x7fffea0dde88>} -
evaluate the function call arguments:
cxx_bind_parameters_in_callwalks over the function arguments (here it's(4)) and evaluates them viacxx_eval_constant_expressionand then saves them into the vectornew_call->bindings. Here that results in<4>. -
look to see if we've already evaluated this call before with the same parameter bindings:
(gdb) p new_call $47 = {fundef = 0x7fffea22cb20, bindings = <tree_vec 0x7fffea22cc40>, result = <tree 0x0>, hash = 4171003643, manifestly_const_eval = true} -
since we haven't evaluated this call before, we need to do so now. Start by remapping the parameters: take our
new_call.bindingswhich is<4>and for each element of this vector:- unshare it,
- put it into the hash map:
ctx->global->values.put (remapped, arg);whereremappedisparm_decl iandargis4. - put the function's
RESULT_DECLinto the map:ctx->global->values.put (res, NULL_TREE);(we have no value for it yet) - evaluate the function's body:
cxx_eval_constant_expression (body)wherebodyis
-
int r = VIEW_CONVERT_EXPR<int>(i) * 2;
return <retval> = VIEW_CONVERT_EXPR<int>(r) + (int) VIEW_CONVERT_EXPR<const int>(g);- note that right before evaluating the body we get
(gdb) p *ctx->global->values.get(res)
$71 = (tree_node *) 0x0
but after it:
(gdb) pge *ctx->global->values.get(res)
50
which is the result:
result = *ctx->global->values.get (res);- now remove the
RESULT_DECLand parameter binding from the hash map - save the result to our
constexpr_call.resultfield:entry->result = cacheable ? result : error_mark_node;and we're done:return result;
- so the result of the call to
foo (4)is50. Now we're back incxx_eval_binary_expressionand ready to get the result and return it:
(gdb) p code
$78 = EQ_EXPR
(gdb) pge lhs
50
(gdb) pge rhs
50
3188 r = fold_binary_loc (loc, code, type, lhs, rhs);
maybe_constant_valuereturns1
constexpris Turing-complete after DR 1454 (reference parameters in constexpr functions)*_constant_init:strictis false (we try to get constant values for more than just C++ constant expressions)*_constant_value:strictis truebuild_date_member_initialization--- DM initialization in constexpr constructor; builds up a pairing of the data member with its initializer__builtin_is_constant_evaluated--- P0595- P0859: a function is needed for constant evaluation if it is a
constexprfunction that is named by an expression that is potentially constant evaluated -> so we need to instantiate anyconstexprfunctions mentioned by the expression:instantiate_constexpr_fnshascp_walk_treewithinstantiate_cx_fn_r, checksDECL_TEMPLOID_INSTANTIATIONand callsinstantiate_declfor constexpr function decls
- can't call
maybe_constant_valueon an unresolved overloaded function (like in PR46903) - N4268: Allow constant evaluation for all non-type template arguments
- in a constexpr function, a parameter is potentially constant when evaluating a call to that function, but it is not constant during parsing of the function; see this patch
is_instantiation_of_constexpr-- if a function is an instantiation of a constexpr functioncp_function_chain->invalid_constexpr-- set for invalid constexpr functions- since this we limit the initial instantiation of all used functions to manifestly-constant-evaluated expressions:
- instantiate_constexpr_fns (r);
+ if (manifestly_const_eval)
+ instantiate_constexpr_fns (r);
DECL_IMMEDIATE_FUNCTION_Pin gdb:p foo.function_decl.common.common.common.common.lang_specific.u.fn.immediate_fn_p
Life begins in c_parse_file:
- create a new main C++ lexer and new C++ parser
- call
cp_parser_translation_unit - call
finish_translation_unit
cp_parser_translation_unit:
-
handle [gram.basic]
-
call
cp_parser_toplevel_declarationuntil EOF -
PR64666 - we accept an assignment in a constant-expression but we shouldn't; see
cp_parser_constant_expression
[class.mem]p7: A complete-class context of a class is:
- function body
- default argument
- default template argument
- noexcept-specifier
- default member initializer
within the member-specification of the class or class template.
And [class.mem]p8: The class is regarded as complete within its complete-class contexts. So we defer parsing to handle this. This is implemented via cp_parser_late_parsing_for_member, cp_parser_late_parsing_nsdmi, cp_parser_late_noexcept_specifier, etc. An unparsed entity is represented by a DEFERRED_PARSE.
TEMPLATE_DECL ~ represents a template definition
| accessor | |
|---|---|
DECL_ARGUMENTS |
template parameter vector |
DECL_TEMPLATE_INFO |
template info |
DECL_VINDEX |
list of instantiations (functions only) |
| class templates | |
|---|---|
DECL_INITIAL |
associated templates |
| non-class templates | |
|---|---|
TREE_TYPE |
type of object to be constructed |
DECL_TEMPLATE_RESULT |
decl for object to be created (e.g., the FUNCTION_DECL |
- deals with types, decls, ...
DECL_P->tsubst_decl- for some things like
integer_type_nodeorNULLPTR_TYPEjust returns the tree - for
INTEGER_TYPE: substituteTYPE_MAX_VALUE - handles e.g.:
TEMPLATE_TYPE_PARM,TREE_LIST,TREE_VEC,POINTER_TYPE,FUNCTION_TYPE,DECLTYPE_TYPE
- does the substitution but doesn't finish the expression
- also deals with
_DECL
- like
tsubst_copyfor expressions, does semantic processing RETURN_EXPR: callsfinish_return_stmtafter recursing on its operandsEXPR_STMT,USING_STMT,DECL_EXPR,BIND_EXPR, ...- calls
finish_* - is now
tsubst_stmt; see r14-4797
- deals with expressions and performs semantic analysis
IDENTIFIER_NODE:finish_id_expressionTEMPLATE_ID_EXPRtsubst_template_args,lookup_template_function- builds a
COMPONENT_REF
INDIRECT_REF:tsubst+build_nopIMPLICIT_CONV_EXPR:tsubst+ recurse on the operand +perform_implicit_conversion- handles various cast expressions
++,--, binary opsCALL_EXPR: depends if it's ADL or not;finish_call_exprCOND_EXPR- handles a
CONSTRUCTORwhichtsubstdoesn't - no
tsubst*function handlesAGGR_INIT_EXPR - is now
tsubst_expr; see r14-4797
- has a
use_spec_tableparm -- look up/insert into the specialization table:spec_hasher::hash,retrieve_specialization
- see [temp.deduct.type], e.g., the expression of a decltype-specifier
- in
unify:
case TYPEOF_TYPE:
case DECLTYPE_TYPE:
case UNDERLYING_TYPE:
/* Cannot deduce anything from TYPEOF_TYPE, DECLTYPE_TYPE,
or UNDERLYING_TYPE nodes. */
return unify_success (explain_p);- the operand of
decltypeis also an unevaluated operand -> NS class members might be named
- when doing initial explicit argument substitution in
fn_type_unification. E.g.,
template<typename T, typename U>
T fn (T t, U u)
{
return t + u;
}
void g ()
{
fn<int>(1, 1);
}in fn_type_unification: we provided the first template argument:
explicit_targs = <int>
fntype = T <T36d> (T, U)
and tf_partial is used:
/* Adjust any explicit template arguments before entering the
substitution context. */
explicit_targs
= (coerce_template_parms (tparms, explicit_targs, fn,
complain|tf_partial,
/*require_all_args=*/false,
/*use_default_args=*/false));and when substituting the explicit args into the function type. See [temp.deduct.general]/2: When an explicit template argument list is specified ... the specified template argument values are substituted for the corresponding template parameters.
tsubst_flags_t ecomplain = complain | tf_partial | tf_fndecl_type;
fntype = tsubst (TREE_TYPE (fn), explicit_targs, ecomplain, NULL_TREE);so now fntype is int <T36e> (int, U). [temp.deduct.general]/5: The resulting substituted and adjusted function type is used as the type of the function template for template argument deduction. To deduce U:
// full_targs = int,
// parms = int, U, void
// args = 1
// DECL_INNERMOST_TEMPLATE_PARMS (fn) = T, U
ok = !type_unification_real (DECL_INNERMOST_TEMPLATE_PARMS (fn),
full_targs, parms, args, nargs, /*subr=*/0,
strict, &checks, explain_p);
// full_targs = targs = int, intAlso: When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in the template parameter list of the template are replaced with the corresponding deduced or default argument values.
and
If type deduction has not yet failed, then all uses of template parameters in the function type are replaced with the corresponding deduced or default argument values.
[temp.deduct.general]/6: At certain points in the template argument deduction process it is necessary to take a function type that makes use of template parameters and replace those template parameters with the corresponding template arguments. This is done at the beginning of template argument deduction when any explicitly specified template arguments are substituted into the function type, and again at the end of template argument deduction when any template arguments that were deduced or obtained from default arguments are substituted.
-
also note that The equivalent substitution in exception specifications is done only when the noexcept-specifier is instantiated, at which point a program is ill-formed if the substitution results in an invalid type or expression.
-
now
// fn = template_decl fn
// targs = int, int
decl = instantiate_template (fn, targs, complain);
// decl = function_decl fn
// TREE_TYPE (decl) = int <T112> (int, int)
// ...
return r; // r == decl- in PR89966 passing
tf_partialpreventeddo_auto_deductionfrom actually replacingauto, sodo_auto_deductionnow clearstf_partial
- tries to find a specialization (either an instantiation or an explicit specialization) of a template with certain arguments
- uses the
decl_specializationsandtype_specializationshash tables (unlessoptimize_specialization_lookup_p) - can use
register_specializationorreregister_specializationto add a specialization - e.g.,
gen_tmpl = most_general_template (tmpl);
// gen_tmpl e.g. "template<class U> template<class V> struct B<U>::C"
// args e.g. <int, V>
spec = retrieve_specialization (gen_tmpl, argvec, hash);- if a tree is a specialization of a template. This is for ordinary explicit specialization and partial specialization of a template class such as:
template <> class C<int>;or
template <class T> class C<T*>;- = 1 implicit instantiation:
CLASSTYPE_IMPLICIT_INSTANTIATION - = 2 partial or explicit specialization:
CLASSTYPE_TEMPLATE_SPECIALIZATION - = 3 explicit instantiation:
CLASSTYPE_EXPLICIT_INSTANTIATION - 1 or 3:
CLASSTYPE_TEMPLATE_INSTANTIATION - in debug output:
use_template=[01] lookup_template_classsetsCLASSTYPE_IMPLICIT_INSTANTIATIONfor a partial instantiation (i.e., for the type of a member template class nested within a template class); required formaybe_process_partial_specializationto work correctly.- to get template arguments from a
RECORD_TYPE, e.g.std::pair<const int&, const int&>: useCLASSTYPE_TI_ARGS. The template parameters ofstd::pair, that istemplate<typename _T1, typename _T2>can be obtained by usingDECL_TEMPLATE_PARMS.
- a variable in a function template will get a
TEMPLATE_DECL(created bystart_decl->push_template_decl->build_template_decl), itsDECL_TEMPLATE_RESULTwill be aVAR_DECL, sovariable_template_pchecks forPRIMARY_TEMPLATE_P:
template<typename> void f() {
extern int foo;
foo = 1; // gets a TEMPLATE_DECL, its DECL_PRIMARY_TEMPLATE
// is TEMPLATE_DECL f, so it's not PRIMARY_TEMPLATE_P
}[temp.deduct.call]- consider:
template <typename T, typename U> // tparms
void fn (T, U) { } // parms
void g () {
fn (1, "hi"); // no targs, 2 args
}fn_type_unificationwithDEDUCE_CALL->type_unification_realwhich has a loop and callsunify_one_argumentfor every P/A pair. Here we have
parms = [template_type_parm T, template_type_parm U]
args = [1, "hi"]
tparms = <T, U> (a vector of TYPE_DECLs, their types are TEMPLATE_TYPE_PARMs, this is what we need to deduce)
targs = <,> (nothing deduced so far)
-
unify_one_argument (T, 1)- take the type of the arg =
int - call
unify (T, int):parmisTEMPLATE_TYPE_PARM T,TEMPLATE_TYPE_LEVEL (parm) == 1,TEMPLATE_TYPE_IDX (parm) == 0- check that the level of
Tmatches the level of the first template parameter - check for mixed types and values, etc.
- if we already have a targ for this index, we're done
- otherwise, save
argtotargsfor index 0, sotargs = <int,>
- take the type of the arg =
-
unify_one_argument (U, "hi")- take the type of the arg =
const char[3] maybe_adjust_types_for_deductionwill adjust it toconst char *- call
unify (U, const char *):parmisTEMPLATE_TYPE_PARM U,TEMPLATE_TYPE_LEVEL (parm) == 1,TEMPLATE_TYPE_IDX (parm) == 1- save
argtotargsfor index 1, sotargs = <int, const char *>
- take the type of the arg =
type_unification_realreturnsunify_success- now consider
template <auto>
void fn () { }
void g () {
fn<42>();
}fn_type_unification->do_auto_deduction->type_unification_real. Here:
parms = [template_type_parm auto]
args = [42]
tparms = <auto> (a TYPE_DECL with type template_type_parm auto, this is what we need to deduce)
targs = <>
- call
unify_one_argument (template_type_parm auto, 42)->unify (template_type_parm auto, int)- both
parmand 0-th tparm have level 2, becausemake_auto_1: use a TEMPLATE_TYPE_PARM with a level one deeper than the actual template parms parmhas index 0targs[0] = arg, sotargs = <int>
- both
Some expressions are never dependent: [temp.dep.expr]p4: literal, sizeof, typeid, noexcept(expr), alignof
template<typename T>
struct A {
void f() { T::template foo<A>(); }
};the template argument for T::foo might be a type or a template
DR 176, makes it easier to refer to the current instantiation
- called for e.g.
template<class T> struct S;
template<class T> struct S { };to detect things like
template<class = int> class C;
template<class = int> class C { }; // error: redefinition of default argumentthough we don't do it for function templates -- bug!
add_template_candidate_real--- ifTMPLcan be successfully instantiated by the given template arguments and the argument list, add it to the candidates; see [temp.over]- template-id aren't reparsed: if
cp_parser_template_idseesCPP_TEMPLATE_ID, it usessaved_checks_value lookup_template_function--- returns aTEMPLATE_ID_EXPRcorresponding to the function + arguments- if it's
BASELINK_P, create aTEMPLATE_ID_EXPRinto itsBASELINK_FUNCTIONS - if it has no type/is
OVERLOAD: useunknown_type_node
- if it's
pending_templates--- a list of templates whose instantiations have been deferredinstantiate_pending_templates,add_pending_template
DECL_FUNCTION_TEMPLATE_P-- if aTEMPLATE_DECLis a function template. Functions templates cannot be partially specialized, checked incheck_explicit_specializationcurrent_template_args-- within the declaration of a template, return the currently active template parameters; is a vectortemplate_parm_scope_p-- if this scope was created to store template parametersbegin_specialization-- after seeingtemplate<>- DR 727: [temp.expl.spec]: An explicit specialization may be declared in any scope in which the corresponding primary template may be defined. We don't implement it yet.
check_specialization_namespace,check_explicit_instantiation_namespace,check_explicit_specialization
- class templates: member functions are instantiated only if they are used
- if a class template has static members, they are instantiated once for each type for which the class template is used
- passing instantiated codes to
tsubst-> crash; see e.g.case FIX_TRUNC_EXPRintsubst_copy_and_build- we used to handle (wrongly)
FIX_TRUNC_EXPRintsubst_copy_and_build, but since r258821 we don't
- we used to handle (wrongly)
- deduction against
braced-init-listwasn't supported until DR 1591 - DR 226: allowed template default arguments of function templates (C++11)
decl_constant_var_p-- if the VAR_DECL's value can be used in a constant expression. Callsmaybe_instantiate_decl (decl)to detect using DECL in its own initializer.check_template_shadow-- checks if a decl shadows a template parameterbuild_value_initdoesn't work in templatesbuild_vec_initin a template builds up a lot of garbage that we'll throw away at the end (see PR93676)
TREE_TYPEis the magic bit-field integral type; the lowered typeunlowered_expr_typeis the declared type of the bitfield (usesDECL_BIT_FIELD_TYPE)- related PRs: PR82165, PR92859, PR87547, PR78908, PR30328, PR65556, PR98043
- e.g.:
struct S { signed int a:17; } x;
// TREE_TYPE (x.a) = <unnamed-signed:17>
// unlowered_expr_type (x.a) = int
enum E { e0, e1, e2 };
struct R { E a : 2; } y;
// TREE_TYPE (y.a) = <unnamed-unsigned:2>
// unlowered_expr_type (y.a) = E
enum class B { A };
struct C { B c : 8; } z;
// TREE_TYPE (z.c) = signed char
// unlowered_expr_type (z.c) = B- integral promotions: [conv.prom]
- unnamed bit-fields are not members
- handles
FN (ARGS)and produces a CALL_EXPR - if FN is a COMPONENT_REF with a BASELINK:
return build_new_method_call (object, member, ...);- if FN is a BASELINK, it's a call to a member function:
result = build_new_method_call (object, fn, args, ...);- if FN is a concept check, call
build_concept_check - when we have a functor, FN will be a VAR_DECL; use this to call
operator ():
result = build_op_call (fn, args, complain);-
TODO
-
present in GCC 2.95, in which it only called
build_x_function_call
- has the
CHECKING_Pcheck in C++17: we check to make sure that we are initializing directly from class prvalues rather than copying them:
/* In C++17 we shouldn't be copying a TARGET_EXPR except into a
potentially-overlapping subobject. */
if (CHECKING_P && cxx_dialect >= cxx17)
gcc_assert (TREE_CODE (arg) != TARGET_EXPR
|| force_elide
/* It's from binding the ref parm to a packed field. */
|| convs[0]->need_temporary_p
|| seen_error ()
/* See unsafe_copy_elision_p. */
|| unsafe_return_slot_p (fa));INIT_EXPRwith aTARGET_EXPRas the RHS = direct-initMODIFY_EXPRwith aTARGET_EXPRas the RHS = copy- for classes but also when converting an integer to a reference type:
convert_like_internal/ck_ref_bind - can express direct-init:
TARGET_EXPR_DIRECT_INIT TARGET_EXPR_DIRECT_INIT_Pmeans there tsubst isn't a temporary involved (-> checking incp_gimplify_exprthat we don't see anyTARGET_EXPRwith that flag set (because they all get folded away bycp_gimplify_init_expr)- should never get into
fold_non_dependent_expr - when the gimplifier encounters the same
TARGET_EXPRtwice, it evaluatesTARGET_EXPR_INITIALthe first time and clears it so that the later evaluation is just the temporary; seegimplify_target_expr - a
TARGET_EXPRwithin aTARGET_EXPRshould be collapsed incp_fold_r TARGET_EXPR_ELIDING_Pset if theTARGET_EXPRis an initializer, not a temporary. Seeset_target_expr_eliding.cp_gimplify_init_exprcheck that aTARGET_EXPR_INITIALcomes from aTARGET_EXPR_ELIDING_P. Done in r13-3175.
- introduced 2003-07-08, gcc-3.4.0, r69130
- type-computation for non-dependent expressions
build_non_dependent_expr- a placeholder for an expression that is not type-dependent, but does occur in a template
- the standard says that we have to compute the types of expressions in templates where possible:
struct A { template<int I> void f(); };
struct B { int f; };
A *g(int);
B *g(double);
template<typename T> void h() { g(2)->f<3>(); }f<int> is a template. We have to get the type of g(2) in order to parse the expr.
- already in GCC 2.95, used in
build_new_1 - represents initialization of an array from another array
- if it represents value-initialization,
VEC_INIT_EXPR_VALUE_INITwill be set - if it's a potential constant expression,
VEC_INIT_EXPR_IS_CONSTEXPRwill be set - built by
build_vec_init_expr. Used to build aTARGET_EXPRtoo, but not anymore build_vec_init_eltbuilds up a single element intialization- used in
perform_member_initwhen initializing an array (see r165976) - used in a defaulted constructor for a class with a non-static data member of array type
- used in
build_array_copywhen creating a closure object for a lambda VEC_INIT_EXPRis handled incp_gimplify_exprsince r152318 -- it usesbuild_vec_initto expand it- doesn't track whether the initialization was direct-init? PR82235
- used when we don't have a
thisparameter to refer to yet - consider:
struct S {
int a;
void *p = this; // #1
};
void g()
{
S a{1}; // #2
}get_nsdmicreates aPLACEHOLDER_EXPRforS::p's NSDMI (#1), so its initializer is(void *) &<PLACEHOLDER_EXPR struct S>.- after we've created a
VAR_DECLfora,store_init_value->replace_placeholdersgetsexp={.a=1, .p=(void *) &<PLACEHOLDER_EXPR struct S>}, obj=a, replaces the PLACEHOLDER_EXPR, as the name suggests, and returns{.a=1, .p=(void *) &a}
- used so that
replace_placeholders_rdoesn't walk into constructors that havePLACEHOLDER_EXPRs related to another object. E.g.,
struct C {};
struct X {
unsigned i;
unsigned n = i;
};
C bar (X x)
{
return {};
}
int main ()
{
C c = bar (X {1});
}- the init for
nis initially((struct X *) this)->i, but we don't have the object yet, so we create aPLACEHOLDER_EXPRand the init is then(&<PLACEHOLDER_EXPR struct X>)->i(cf.get_nsdmi) CONSTRUCTOR_PLACEHOLDER_BOUNDARYis set on{NON_LVALUE_EXPR <1>}:process_init_constructor_recordis processing the initializer{NON_LVALUE_EXPR <1>}forX, it walks all members ofXand it sees that the NSDMI forn(which is(&<PLACEHOLDER_EXPR struct X>)->i) has aPLACEHOLDER_EXPR{NON_LVALUE_EXPR <1>}is turned into{.i=1, .n=(&<PLACEHOLDER_EXPR struct X>)->i}inprocess_init_constructor_record- then
replace_placeholders_rwill not walk into the constructor when it's called fromstore_init_valuewithexp=bar (TARGET_EXPR <D.2396, {.i=1, .n=(&<PLACEHOLDER_EXPR struct X>)->i}>), obj=c. If it did, we'd crash, because we'd attempt to substitute aPLACEHOLDER_EXPRof typeXwith an object of typeC. (Note that here*t != d->exp, see below.d->expis the wholebar(...)call,*tonly the constructor sub-expression.) - the
PLACEHOLDER_EXPR Xis replaced withD.2432incp_gimplify_init_exprwhich getsD.2432 = {.i=1, .n=(&<PLACEHOLDER_EXPR struct X>)->i}. TheCONSTRUCTOR_PLACEHOLDER_BOUNDARYis still set, but this time in:
if ((CONSTRUCTOR_PLACEHOLDER_BOUNDARY (*t)
&& *t != d->exp)
|| d->pset->add (*t))*t actually is d->exp (= the outermost expression).
- printed by
pp_cxx_parameter_mappingordump_substitution - see also
pp_cxx_template_argument_list - print the buffer:
(gdb) p pp.buffer.formatted_obstack
- given a function template:
template<typename T>
void foo () { }and &foo<int> in the code, resolve_nondeduced_context resolves
&TEMPLATE_ID_EXPR <foo, int> (type unknown type) to foo (ADDR_EXPR of type void (*<T358>) (void)).
- it instantiates
fooif there are no dependent template arguments - see DR 115
finalize_type_sizecan (un)set it- a related ABI issue
struct
{
void X::<T34e> (struct X *) * __pfn;
long int __delta;
}- is just
OFFSET_TYPE - null pointer-to-data-member represented by -1: cf.
null_member_pointer_value_pandcp_convert_to_pointer
%A function argument-list.
%C tree code.
%D declaration.
%E expression.
%F function declaration.
%G gcall *
%H type difference (from).
%I type difference (to).
%K tree
%L language as used in extern "lang".
%O binary operator.
%P function parameter whose position is indicated by an integer.
%Q assignment operator.
%S substitution (template + args)
%T type.
%V cv-qualifier.
%X exception-specification.
- should be used on a pointer type, not a
CALL_EXPR
- use to look through the implicit
INDIRECT_REF
- type alias
- like
typedef
alias-declaration:
using identifier attribute-specifier-seq [opt] = defining-type-id ;
- alias template
- a template-declaration in which the declaration is an alias-declaration
- refers to a family of types
- [temp.alias]
using/typedefrepresented by aTYPE_DECLusing name = typehasTYPE_DECL_ALIAS_Pset- can look at its
DECL_NAMEandDECL_ORIGINAL_TYPE
- introduced in here
- used in
dependent_alias_template_spec_p
D0,D1, ..., seewrite_special_name_destructor:
if (DECL_DELETING_DESTRUCTOR_P (dtor))
write_string ("D0");
else if (DECL_BASE_DESTRUCTOR_P (dtor))
write_string ("D2");
else if (DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (dtor))
/* This is the old-style "[unified]" destructor.
In some cases, we may emit this function and call
it from the clones in order to share code and save space. */
write_string ("D4");
else
{
gcc_assert (DECL_COMPLETE_DESTRUCTOR_P (dtor));
write_string ("D1");
}- for
D4, see e.g. this
grok_op_properties--- checks a declaration of an overloaded or conversion operatorgrok_ctor_properties--- checks if a constructor has the correct formgrok_reference_init--- handles initialization of referencesgrok_special_member_properties--- setsTYPE_HAS_*flags likeTYPE_HAS_USER_CONSTRUCTORAGGR_INIT_EXPR--- useful where we want to defer actually building up the code to manipulate the object until we know which object it is we're dealing withsimplify_aggr_init_expr---AGGR_INIT_EXPR-->CALL_EXPRdefaultable_fn_check--- if a function can be explicitly defaultedconvert_nontype_argument--- follows [temp.arg.nontype]; called twice for each targ = double coercion (finish_template_type+instantiate_template_class)do_class_deduction--- C++17 class deductiondo_auto_deduction--- replaceautoin the type with the type deduced from the initializerprocess_outer_var_ref--- has theodr_useparam, true if it's called frommark_use, complain about the use of constant variables; an ODR-use of an outer automatic variables causes an errorREFERENCE_REF_P--- an implicitINDIRECT_EXPRfromconvert_to_referenceconvert_from_reference---x [type T&]-->*x [type T]build_temp--- can trigger overload resolution by way ofbuild_special_member_call-->build_new_method_call-->add_candidatesIDENTIFIER_BINDING--- innermostcxx_bindingfor the identifierbuild_class_member_access_expr--- buildsobject.member, whereobjectis an expression,memberis a declaration/baselinkcopy_nodedoesn't copy language-specific parts, butcopy_decldoesbuild_non_dependent_exprusesif (flag_checking > 1 ...) { fold_non_dependent_expr ();}- prefix/postfix
operator++()distinguished by a hiddenintargument, added inbuild_new_op_1:
if (code == POSTINCREMENT_EXPR || code == POSTDECREMENT_EXPR)
{
arg2 = integer_zero_node;
arg2_type = integer_type_node;
}digest_init--{1, 2}->{.i = 1, .j = 2 }
- C++98 doesn't allow forming a reference to a reference; in C++11 it just collapses to a single reference; see DR 106
- implicit move --- [class.copy.elision]
- destructors don't have names: DR 2069; you name a dtor by
~type-namewhere thetype-namenames the type - pure virtual function API: only called if the user calls a non-overriden pure virtual function (~ UB); will terminate
extern "C" void __cxa_pure_virtual ();- not all function declarations can be redeclared: [basic.scope.scope], [class.mem]p5
- braced-init-lists aren't expressions, so can't use
b ? {1,0} : {0,1} - default argument ~ a separate definition: [temp.decls]
- functions are not modifiable even though they are lvalues
- function arguments of reference types aliasing: [basic.lval]/11
- A Module System for C++: P0142
- Modules TS: N4720
- Merging Modules: P1103
- Davis's SO answer
- another good resource
- module mapper: P1184
- Make Me A Module: P1602
- linkage promotion discussed here, fixed by P1498?
- inline and modules: P1604
- std::optional: C++17, an optional contained valued
- has monadic ops in C++23:
and_then,transform,or_else
- has monadic ops in C++23:
- std::variant: C++17, a type-safe union, can use
std::monostate(~ empty) - std::piecewise_construct: used to disambiguate between different functions that take two tuple arguments
- updating e.g.
doc/html/manual/status.htmlmeans updatingdoc/xml/manual/status_cxx2023.xmland then regenerating the former file - you need the following packages:
libxml2,libxslt,dblatex,docbook5-style-xsl,docbook5-schemas,docbook2X - and then do
make doc-html-docbook-regeneratein the libstdc++ build dir, that will generate the html files and copy them back into the source tree - see this
- always collect:
--param ggc-min-expand=0 --param ggc-min-heapsize=0
- It's customary to build up the list and then
nreverseit rather than usechainonin the loop, which means traversing the whole list each time you add another node. void_list_node(and similar) is a shared list node; we shouldn't change it
- after processing the command-line options, we call
cpp_read_main_file->_cpp_find_file->find_file_in_dir->open_filewhich does:
file->fd = open (file->path, O_RDONLY | O_NOCTTY | O_BINARY, 0666);- the source file is read in
read_file->read_file_guts:
while ((count = read (file->fd, buf + total, size - total)) > 0)
{
// ...
}- the input buffer is then converted in
_cpp_convert_inputfrom the input charset to the source character set, if needed. This can be done usingiconv. - then close the file descriptor after reading:
close (file->fd); - the buffer is kept in
_cpp_file::buffer - this is used for default includes like
stdc-predef.h, seecpp_push_default_include - then we can actually compile the file:
c_common_parse_file->c_parse_file
- in C++:
cp_lexer_new_mainreads all tokens usingcp_lexer_get_preprocessor_token:
/* Get the remaining tokens from the preprocessor. */
while (tok->type != CPP_EOF)
{
if (filter)
/* Process the previous token. */
module_token_lang (tok->type, tok->keyword, tok->u.value,
tok->location, filter);
tok = vec_safe_push (lexer->buffer, cp_token ());
cp_lexer_get_preprocessor_token (C_LEX_STRING_NO_JOIN, tok);
}cp_lexer_get_preprocessor_tokenusesc_lex_with_flags->cpp_get_token_with_location- then we have the tokens saved in
lexer->buffer:vec<cp_token, va_gc> *buffer; - peek next token:
cp_lexer_peek_token - return the next token and consume it:
cp_lexer_consume_token - purge tokens:
cp_lexer_purge_tokens_after, used incp_parser_check_for_invalid_template_id
- should not be used in production: see this
- plugins location: use
--print-file-name=pluginbut that only works with the installed compiler:make installcreates the plugin directory, it's not in the build directory. Can use-Bto point to it though. find_filemust be able to find "plugin"- configure option:
--enable-plugin
- GCC 8 ABI bugs: PR87137 + PR86094
- GCC 9 ABI libstdc++ issue with nested
std::pair: PR87822 - gcc-1.42:
cccp.c:
/*
* the behavior of the #pragma directive is implementation defined.
* this implementation defines it as follows.
*/
do_pragma ()
{
close (0);
if (open ("/dev/tty", O_RDONLY, 0666) != 0)
goto nope;
close (1);
if (open ("/dev/tty", O_WRONLY, 0666) != 1)
goto nope;
execl ("/usr/games/hack", "#pragma", 0);
execl ("/usr/games/rogue", "#pragma", 0);
execl ("/usr/new/emacs", "-f", "hanoi", "9", "-kill", 0);
execl ("/usr/local/emacs", "-f", "hanoi", "9", "-kill", 0);
nope:
fatal ("You are in a maze of twisty compiler features, all different");
}- libgcc unwinder has a global lock:
_Unwind_Find_FDEhasobject_mutex cp/g++spec.c--lang_specific_driver, adds-lstdc++usinggenerate_option- multi-target toolchain email
- dump clas info:
-fdump-lang-class - clang/gcc interop problems:
- used to implement
std::addressof - like
&but works even when the type has an overloaded operator&(smart pointers):
struct S { int operator&() { return 42; } };
int main ()
{
S s;
auto p = &s; // invokes operator&
__builtin_printf ("%d\n", p); // prints 42
auto q = __builtin_addressof (s);
__builtin_printf ("%p\n", q); // prints the addr
}- handles more than
__builtin_object_size - used in
_FORTIFY_SOURCE=3and-fsanitize=object-size
C++:
- run the testsuite with garbage collection at every opportunity:
make check-g++-strict-gc - run the testsuite in all standard conformance levels:
make check-c++-all -m32testing:RUNTESTFLAGS="--target_board=unix\{-m32,-m64\} dg.exp=foo.C"--enable-symvers=gnu-versioned-namespace
PDP-11: --target=pdp11-aout
AARCH64: --target=aarch64-linux-gnu target_alias=aarch64-linux-gnu
ARM: --target=arm-none-eabi
PPC64LE: --target=powerpc64le-unknown-linux-gnu
- a hook to dump the preprocessed source on a crash (as
/tmp/cc*out), and then use something like:
BuildRequires: sharutils
and then:
make || ( tar cf - /tmp/cc*.out | bzip2 -9 | uuencode cc.tar.bz2 )
which stashes a uuencoded form of the tarball into the Koji build log
GCC 11 emits debuginfo for external functions too (early debug because of LTO). This was introduced in PR96383, which had unresolved issues: PR97060 (fixed now). E.g.:
extern void f(); // generates .string "_Z1fv"
int main() {
f ();
}IPA-ICF can make backtraces wrong: PR63572
On Linux GCC emits .note.GNU-stack sections to mark the code as not needing executable stack; if that section is missing, it's unknown and code linking in such .o objects can then make the program require executable stack. Assembly files need to be marked manually -- e.g. various *.S files in libgcc:
#if defined(__ELF__) && defined(__linux__)
.section .note.GNU-stack,"",@progbits
.previous
#endif- newer glibc architectures should use empty crti/crtn (
sysdeps/generic/crt[in].S) because they use init_array/fini_array exclusively.
- may no longer be a compile-time constant
- see this commit:
If _SC_SIGSTKSZ_SOURCE or _GNU_SOURCE are defined, MINSIGSTKSZ and SIGSTKSZ
are redefined as
/* Default stack size for a signal handler: sysconf (SC_SIGSTKSZ). */
# undef SIGSTKSZ
# define SIGSTKSZ sysconf (_SC_SIGSTKSZ)
/* Minimum stack size for a signal handler: SIGSTKSZ. */
# undef MINSIGSTKSZ
# define MINSIGSTKSZ SIGSTKSZ
Compilation will fail if the source assumes constant MINSIGSTKSZ or
SIGSTKSZ.
- libsanitizer had to be fixed like this:
- // SIGSTKSZ is not enough.
- static const uptr kAltStackSize = SIGSTKSZ * 4;
- return kAltStackSize;
+ // Note: since GLIBC_2.31, SIGSTKSZ may be a function call, so this may be
+ // more costly that you think. However GetAltStackSize is only call 2-3 times
+ // per thread so don't cache the evaluation.
+ return SIGSTKSZ * 4;
- try using
-Wl,--no-keep-memory -Wl,--reduce-memory-overheads - or maybe
ld -r?
./as-new -o m.o m.s --64
-
open
m.oinoutput_file_create- calls
bfd_openwto open a file using the formatTARGET_FORMATand returns abfd bfd_set_formatsets format tobfd_object
- calls
-
assemble
m.sinperform_an_assembly_pass:read_a_source_fileopensm.susinginput_file_open; read the first char withgetc, if it's not#thenungetcit. Then keep reading: read stuff like.file,.globl,.type.- when we read something like
pushq %rbp, callassemble_one->md_assemblewhich is the machine-dependent assembler, on x86_64 this function is defined ingas/config/tc-i386.c.md_assemblewillparse_insnparse_operands- save stuff into
struct _i386_insn - find a template that matches the given insns:
match_template - check if the prefix is valid
- actually output the insn:
output_insn
-
after
match_templatewe can see the opcode:
(gdb) p/x i.tm.base_opcode
$21 = 0x50
(gdb) p i.tm.name
$23 = 0x50de5e "push"
the basic opcode should match the one here; depending on its operand a certain value is added to it. Another example:
(gdb) f
#0 md_assemble (line=<optimized out>, line@entry=0x5c2eaa "movq %rsp,%rbp") at /home/mpolacek/src/binutils-gdb/gas/config/tc-i386.c:4770
4770 if (sse_check != check_none
(gdb) p/x i.tm.base_opcode
$3 = 0x89
(see mov)
- use
pteto print aninsn_template md_begininitializes theop_hashhash tableopcodes/i386-tbl.his the optable generated byi386-gen, contains the basic opcodes etc.opcodes/i386-opc.tblis the table, it's where new instructions get added
- all spec files: here