Message ID | 01020191c70b82f6-b6eab845-f923-4389-bef2-f189b8c4ca43-000000@eu-west-1.amazonses.com |
---|---|
State | New |
Headers | show |
Series | c++: Properly mangle CONST_DECL without a INTEGER_CST value [PR116511] | expand |
On 9/6/24 7:15 AM, Simon Martin wrote: > We ICE upon the following *valid* code when mangling the requires > clause > > === cut here === > template <int> struct s1 { > enum { e1 = 1 }; > }; > template <int t2> struct s2 { > enum { e1 = s1<t2>::e1 }; > s2() requires(0 != e1) {} > }; > s2<8> a; > === cut here === > > The problem is that the mangler wrongly assumes that the DECL_INITIAL of > a CONST_DECL is always an INTEGER_CST, and blindly passes it to > write_integer_cst. > > I assume we should be able to actually compute the value of e1 and use > it when mangling, however from my investigation, it seems to be a pretty > involved change. > > What's clear however is that we should not try to write a non-literal as > a literal. This patch adds a utility function to determine whether a > tree is a literal as per the definition in the ABI, and uses it to only > call write_template_arg_literal when we actually have a literal in hand. > > Note that I had to change the expectation of an existing test, that was > expecting "[...]void (AF::*)(){}[...]" and now gets an equivalent > "[...](void (AF::*)())0[...]" (and FWIW is what clang and icx give; see > https://godbolt.org/z/hnjdeKEhW). Unfortunately we need to provide backward bug compatibility for -fabi-version=14, so this change needs to check abi_version_at_least (15). > +/* Determine whether T is a literal per section 5.1.6.1 of the CXX ABI. */ > + > +static bool > +literal_p (const tree t) > +{ > + if ((TREE_TYPE (t) && NULLPTR_TYPE_P (TREE_TYPE (t))) This looks wrong; a random expression with type nullptr_t is not a literal, and can be instantiation-dependent. And I don't see any test of mangling such a thing. Jason
Hi Jason, On Thu Apr 10, 2025 at 10:42 PM CEST, Jason Merrill wrote: > On 9/6/24 7:15 AM, Simon Martin wrote: >> We ICE upon the following *valid* code when mangling the requires >> clause >> >> === cut here === >> template <int> struct s1 { >> enum { e1 = 1 }; >> }; >> template <int t2> struct s2 { >> enum { e1 = s1<t2>::e1 }; >> s2() requires(0 != e1) {} >> }; >> s2<8> a; >> === cut here === >> >> The problem is that the mangler wrongly assumes that the DECL_INITIAL of >> a CONST_DECL is always an INTEGER_CST, and blindly passes it to >> write_integer_cst. >> >> I assume we should be able to actually compute the value of e1 and use >> it when mangling, however from my investigation, it seems to be a pretty >> involved change. >> >> What's clear however is that we should not try to write a non-literal as >> a literal. This patch adds a utility function to determine whether a >> tree is a literal as per the definition in the ABI, and uses it to only >> call write_template_arg_literal when we actually have a literal in hand. >> >> Note that I had to change the expectation of an existing test, that was >> expecting "[...]void (AF::*)(){}[...]" and now gets an equivalent >> "[...](void (AF::*)())0[...]" (and FWIW is what clang and icx give; see >> https://godbolt.org/z/hnjdeKEhW). > > Unfortunately we need to provide backward bug compatibility for > -fabi-version=14, so this change needs to check abi_version_at_least (15). Good point, ack. >> +/* Determine whether T is a literal per section 5.1.6.1 of the CXX ABI. */ >> + >> +static bool >> +literal_p (const tree t) >> +{ >> + if ((TREE_TYPE (t) && NULLPTR_TYPE_P (TREE_TYPE (t))) > > This looks wrong; a random expression with type nullptr_t is not a > literal, and can be instantiation-dependent. And I don't see any test > of mangling such a thing. TBH I think there might be more than just this wrong with this patch :-) I have been flip-flopping between "it's wrong to just mangle the expression" and "but I don't think we can do much better" and never settled on one; that's why I never pinged this 6+ month old patch. Is the approach this took actually valid? I think that in an ideal world, the enum value would have been tsubst'd (or we'd have all we need to tsubst it) when we mangle, and I tried to hook things up so that it happens, but I never succeeded. Simon
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc index 46dc6923add..8279c3fe177 100644 --- a/gcc/cp/mangle.cc +++ b/gcc/cp/mangle.cc @@ -223,6 +223,7 @@ static void write_method_parms (tree, const int, const tree); static void write_class_enum_type (const tree); static void write_template_args (tree, tree = NULL_TREE); static void write_expression (tree); +static bool literal_p (const tree); static void write_template_arg_literal (const tree); static void write_template_arg (tree); static void write_template_template_arg (const tree); @@ -3397,8 +3398,7 @@ write_expression (tree expr) || code == TEMPLATE_PARM_INDEX) write_template_param (expr); /* Handle literals. */ - else if (TREE_CODE_CLASS (code) == tcc_constant - || code == CONST_DECL) + else if (literal_p (expr)) write_template_arg_literal (expr); else if (code == EXCESS_PRECISION_EXPR && TREE_CODE (TREE_OPERAND (expr, 0)) == REAL_CST) @@ -3946,6 +3946,29 @@ write_expression (tree expr) } } +/* Determine whether T is a literal per section 5.1.6.1 of the CXX ABI. */ + +static bool +literal_p (const tree t) +{ + if ((TREE_TYPE (t) && NULLPTR_TYPE_P (TREE_TYPE (t))) + || null_member_pointer_value_p (t)) + return true; + else + switch (TREE_CODE (t)) + { + case CONST_DECL: + return literal_p (DECL_INITIAL (t)); + case INTEGER_CST: + case REAL_CST: + case STRING_CST: + case COMPLEX_CST: + return true; + default: + return false; + } +} + /* Literal subcase of non-terminal <template-arg>. "Literal arguments, e.g. "A<42L>", are encoded with their type @@ -3956,6 +3979,8 @@ write_expression (tree expr) static void write_template_arg_literal (const tree value) { + gcc_assert (literal_p (value)); + if (TREE_CODE (value) == STRING_CST) /* Temporarily mangle strings as braced initializer lists. */ write_string ("tl"); @@ -4113,9 +4138,7 @@ write_template_arg (tree node) else if (code == TEMPLATE_DECL) /* A template appearing as a template arg is a template template arg. */ write_template_template_arg (node); - else if ((TREE_CODE_CLASS (code) == tcc_constant && code != PTRMEM_CST) - || code == CONST_DECL - || null_member_pointer_value_p (node)) + else if (literal_p (node)) write_template_arg_literal (node); else if (code == EXCESS_PRECISION_EXPR && TREE_CODE (TREE_OPERAND (node, 0)) == REAL_CST) diff --git a/gcc/testsuite/g++.dg/abi/mangle72.C b/gcc/testsuite/g++.dg/abi/mangle72.C index 9581451c25d..fd7d6cb51ad 100644 --- a/gcc/testsuite/g++.dg/abi/mangle72.C +++ b/gcc/testsuite/g++.dg/abi/mangle72.C @@ -89,7 +89,7 @@ void k00 (F<D{{ 0, 0 }}>) { } // { dg-final { scan-assembler "_Z3k001FIXtl1DEEE" } } void k0x (F<D{{ 0, &AF::f }}>) { } -// { dg-final { scan-assembler "_Z3k0x1FIXtl1DtlA2_M2AFFvvEtlS3_EtlS3_adL_ZNS1_1fEvEEEEEE" } } +// { dg-final { scan-assembler "_Z3k0x1FIXtl1DtlA2_M2AFFvvELS3_0EtlS3_adL_ZNS1_1fEvEEEEEE" } } void kx_ (F<D{{ &AF::f }}>) { } // { dg-final { scan-assembler "_Z3kx_1FIXtl1DtlA2_M2AFFvvEtlS3_adL_ZNS1_1fEvEEEEEE" } } diff --git a/gcc/testsuite/g++.dg/abi/mangle80.C b/gcc/testsuite/g++.dg/abi/mangle80.C new file mode 100644 index 00000000000..983f35cc440 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/mangle80.C @@ -0,0 +1,13 @@ +// PR c++/116511 +// { dg-do compile { target c++20 } } +// { dg-additional-options -fabi-compat-version=0 } +template <int> struct s1 { + enum { e1 = 1 }; +}; +template <int t2> struct s2 { + enum { e1 = s1<t2>::e1 }; + s2() requires(0 != e1) {} +}; + +// { dg-final { scan-assembler "_ZN2s2ILi8EEC1EvQneLi0EL_ZNS_IXT_EEUt_2e1EE" } } +s2<8> a;