cpp_define (pfile, "__cpp_enumerator_attributes=201411L");
cpp_define (pfile, "__cpp_nested_namespace_definitions=201411L");
cpp_define (pfile, "__cpp_fold_expressions=201603L");
- cpp_define (pfile, "__cpp_nontype_template_args=201411L");
+ if (cxx_dialect <= cxx17)
+ cpp_define (pfile, "__cpp_nontype_template_args=201411L");
cpp_define (pfile, "__cpp_range_based_for=201603L");
if (cxx_dialect <= cxx17)
cpp_define (pfile, "__cpp_constexpr=201603L");
cpp_define (pfile, "__cpp_consteval=201811L");
cpp_define (pfile, "__cpp_constinit=201907L");
cpp_define (pfile, "__cpp_deduction_guides=201907L");
+ cpp_define (pfile, "__cpp_nontype_template_args=201911L");
cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806L");
cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");
cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L");
write_expression (member);
}
+/* EXPR is a base COMPONENT_REF; write the minimized base conversion path for
+ converting to BASE, or just the conversion of EXPR if BASE is null.
+
+ "Given a fully explicit base path P := C_n -> ... -> C_0, the minimized base
+ path Min(P) is defined as follows: let C_i be the last element for which the
+ conversion to C_0 is unambiguous; if that element is C_n, the minimized path
+ is C_n -> C_0; otherwise, the minimized path is Min(C_n -> ... -> C_i) ->
+ C_0."
+
+ We mangle the conversion to C_i if it's different from C_n. */
+
+static bool
+write_base_ref (tree expr, tree base = NULL_TREE)
+{
+ if (TREE_CODE (expr) != COMPONENT_REF)
+ return false;
+
+ tree field = TREE_OPERAND (expr, 1);
+
+ if (TREE_CODE (field) != FIELD_DECL || !DECL_FIELD_IS_BASE (field))
+ return false;
+
+ tree object = TREE_OPERAND (expr, 0);
+
+ tree binfo = NULL_TREE;
+ if (base)
+ {
+ tree cur = TREE_TYPE (object);
+ binfo = lookup_base (cur, base, ba_unique, NULL, tf_none);
+ }
+ else
+ /* We're at the end of the base conversion chain, so it can't be
+ ambiguous. */
+ base = TREE_TYPE (field);
+
+ if (binfo == error_mark_node)
+ {
+ /* cur->base is ambiguous, so make the conversion to
+ last explicit, expressed as a cast (last&)object. */
+ tree last = TREE_TYPE (expr);
+ write_string (OVL_OP_INFO (false, CAST_EXPR)->mangled_name);
+ write_type (build_reference_type (last));
+ write_expression (object);
+ }
+ else if (write_base_ref (object, base))
+ /* cur->base is unambiguous, but we had another base conversion
+ underneath and wrote it out. */;
+ else
+ /* No more base conversions, just write out the object. */
+ write_expression (object);
+
+ return true;
+}
+
/* <expression> ::= <unary operator-name> <expression>
::= <binary operator-name> <expression> <expression>
::= <expr-primary>
ob = TREE_OPERAND (ob, 0);
write_expression (ob);
}
+ else if (write_base_ref (expr))
+ return;
else if (!is_dummy_object (ob))
{
write_string ("dt");
}
/* Subroutine of convert_nontype_argument, to check whether EXPR, as an
- argument for TYPE, points to an unsuitable object. */
+ argument for TYPE, points to an unsuitable object.
+
+ Also adjust the type of the index in C++20 array subobject references. */
static bool
invalid_tparm_referent_p (tree type, tree expr, tsubst_flags_t complain)
{
tree decl = TREE_OPERAND (expr, 0);
+ if (cxx_dialect >= cxx20)
+ while (TREE_CODE (decl) == COMPONENT_REF
+ || TREE_CODE (decl) == ARRAY_REF)
+ {
+ tree &op = TREE_OPERAND (decl, 1);
+ if (TREE_CODE (decl) == ARRAY_REF
+ && TREE_CODE (op) == INTEGER_CST)
+ /* Canonicalize array offsets to ptrdiff_t; how they were
+ written doesn't matter for subobject identity. */
+ op = fold_convert (ptrdiff_type_node, op);
+ decl = TREE_OPERAND (decl, 0);
+ }
+
if (!VAR_P (decl))
{
if (complain & tf_error)
decl);
return true;
}
- else if (!same_type_ignoring_top_level_qualifiers_p
- (strip_array_types (TREE_TYPE (type)),
- strip_array_types (TREE_TYPE (decl))))
+ else if (cxx_dialect < cxx20
+ && !(same_type_ignoring_top_level_qualifiers_p
+ (strip_array_types (TREE_TYPE (type)),
+ strip_array_types (TREE_TYPE (decl)))))
{
if (complain & tf_error)
error ("the address of the %qT subobject of %qD is not a "
template<const char *s> class Y {};
template<const std::type_info &> class Z {};
-X<&s.m> x7; // { dg-error "3:.& s.S::m. is not a valid template argument" }
+X<&s.m> x7; // { dg-error "3:.& s.S::m. is not a valid template argument" "" { target c++17_down } }
Y<"foo"> y1; // { dg-error "string literal" }
Z<typeid(p)> z1; // { dg-error "" }
-// { dg-do compile { target c++17 } }
+// { dg-do compile { target c++17_only } }
#ifndef __cpp_nontype_template_args
#error __cpp_nontype_template_args not defined
#ifndef __cpp_nontype_template_args
# error "__cpp_nontype_template_args"
-#elif __cpp_nontype_template_args != 201411
-# error "__cpp_nontype_template_args != 201411"
+#elif __cpp_nontype_template_args != 201911
+# error "__cpp_nontype_template_args != 201911"
#endif
#ifndef __cpp_hex_float
--- /dev/null
+// { dg-do compile { target c++20 } }
+
+template <auto N> struct A {};
+template <class,class> struct assert_same;
+template <class T> struct assert_same<T,T> {};
+
+#define TEQ(X,Y) static_assert(__is_same(A<(X)>,A<(Y)>))
+#define TNEQ(X,Y) static_assert(!__is_same(A<(X)>,A<(Y)>))
+
+struct C { int i; };
+
+struct B: C
+{
+ int j[3];
+} b;
+
+// { dg-final { scan-assembler _Z1f1AIXaddtL_Z1bE1iEE } }
+void f(A<&b.i>) {}
+TEQ(&b.i,&((C*)&b)->i);
+
+// { dg-final { scan-assembler _Z1g1AIXadixdtL_Z1bE1jLl1EEE } }
+void g(A<&b.j[0]+1>) {}
+TEQ(&b.j[1],&b.j[1]);
+TEQ(&b.j[1],&b.j[0]+1);
+TNEQ(&b.j[1],&b.j[0]);
--- /dev/null
+// { dg-do compile { target c++20 } }
+// { dg-additional-options -Wno-inaccessible-base }
+
+struct Base { int i; };
+template <int N> struct Derived : Derived<N-1>, Base {};
+template <> struct Derived<0> : Base {};
+
+template <int* P> struct A { };
+
+Derived<4> d;
+void f(A<&((Derived<0>&)d).i>) {}
+
+// { dg-final { scan-assembler _Z1f1AIXaddtcvR7DerivedILi0EEL_Z1dE1iEE } }
template<const B* b> class D {};
template<B* b> class E {};
-template<const B* b> void f(D<b> &, C<static_cast<const A*>(b)> &) {} // { dg-error "" }
+template<const B* b> void f(D<b> &, C<static_cast<const A*>(b)> &) {} // { dg-error "" "" { target { ! c++20 } } }
template<const B* b> void g(D<b> &, E<const_cast<B*>(b)> &) {} // { dg-error "" "" { target { ! c++11 } } }
B b;
int main()
{
- C<static_cast<const A*>(&b)> c; // { dg-error "" }
+ C<static_cast<const A*>(&b)> c; // { dg-error "" "" { target c++17_down } }
D<&b> d;
E<const_cast<B*>(&b)> e; // { dg-error "" "" { target { ! c++11 } } }
- f(d, c); // { dg-error "" "" { target c++11 } }
+ f(d, c); // { dg-error "" "" { target { c++11 && { ! c++20 } } } }
g(d, e);
}
int a[10];
struct S { int m; static int s; } s;
-X<&a[2]> x3; // { dg-error "3:.& a\\\[2\\\]. is not a valid template argument" "" { target c++17 } }
+X<&a[2]> x3; // { dg-error "3:.& a\\\[2\\\]. is not a valid template argument" "" { target c++17_only } }
// { dg-error "" "" { target c++14_down } .-1 }
-X<&s.m> x4; // { dg-error "3:.& s.S::m. is not a valid template argument" "" { target c++17 } }
+X<&s.m> x4; // { dg-error "3:.& s.S::m. is not a valid template argument" "" { target c++17_only } }
// { dg-error "" "" { target c++14_down } .-1 }
X<&s.s> x5; // { dg-error "" "" { target { ! c++17 } } } &S::s must be used
X<&S::s> x6; // OK: address of static member