[c++20] For P1907R1: Add checking for structural types for non-type

template parameters.

No support for the new kinds of non-type template argument yet.

This is not entirely NFC for prior language modes: we have historically
incorrectly accepted rvalue references as the types of non-type template
parameters. Such invalid code is now rejected.
This commit is contained in:
Richard Smith 2020-09-20 20:10:02 -07:00
parent 1d782c2987
commit 0cd73dbe2c
12 changed files with 375 additions and 23 deletions

View File

@ -210,6 +210,9 @@ FIELD(DefaultedDestructorIsConstexpr, 1, NO_MERGE)
/// member or base class of non-literal or volatile type.
FIELD(HasNonLiteralTypeFieldsOrBases, 1, NO_MERGE)
/// True if this class is a structural type, assuming it is a literal type.
FIELD(StructuralIfLiteral, 1, NO_MERGE)
/// Whether we have a C++11 user-provided default constructor (not
/// explicitly deleted or defaulted).
FIELD(UserProvidedDefaultConstructor, 1, NO_MERGE)

View File

@ -1396,6 +1396,11 @@ public:
hasTrivialDefaultConstructor());
}
/// Determine whether this is a structural type.
bool isStructural() const {
return isLiteral() && data().StructuralIfLiteral;
}
/// If this record is an instantiation of a member class,
/// retrieves the member class from which it was instantiated.
///

View File

@ -1927,6 +1927,9 @@ public:
/// (C++11 [basic.types]p10)
bool isLiteralType(const ASTContext &Ctx) const;
/// Determine if this type is a structural type, per C++20 [temp.param]p7.
bool isStructuralType() const;
/// Test if this type is a standard-layout type.
/// (C++0x [basic.type]p9)
bool isStandardLayoutType() const;

View File

@ -4423,6 +4423,28 @@ def note_template_nontype_parm_prev_declaration : Note<
"previous non-type template parameter with type %0 is here">;
def err_template_nontype_parm_bad_type : Error<
"a non-type template parameter cannot have type %0">;
def err_template_nontype_parm_bad_structural_type : Error<
"a non-type template parameter cannot have type %0 before C++20">;
def err_template_nontype_parm_incomplete : Error<
"non-type template parameter has incomplete type %0">;
def err_template_nontype_parm_not_literal : Error<
"non-type template parameter has non-literal type %0">;
def err_template_nontype_parm_rvalue_ref : Error<
"non-type template parameter has rvalue reference type %0">;
def err_template_nontype_parm_not_structural : Error<
"type %0 of non-type template parameter is not a structural type">;
def note_not_structural_non_public : Note<
"%0 is not a structural type because it has a "
"%select{non-static data member|base class}1 that is not public">;
def note_not_structural_mutable_field : Note<
"%0 is not a structural type because it has a mutable "
"non-static data member">;
def note_not_structural_rvalue_ref_field : Note<
"%0 is not a structural type because it has a non-static data member "
"of rvalue reference type">;
def note_not_structural_subobject : Note<
"%0 is not a structural type because it has a "
"%select{non-static data member|base class}1 of non-structural type %2">;
def warn_cxx14_compat_template_nontype_parm_auto_type : Warning<
"non-type template parameters declared with %0 are incompatible with C++ "
"standards before C++17">,
@ -4542,6 +4564,8 @@ def err_non_type_template_arg_subobject : Error<
"non-type template argument refers to subobject '%0'">;
def err_non_type_template_arg_addr_label_diff : Error<
"template argument / label address difference / what did you expect?">;
def err_non_type_template_arg_unsupported : Error<
"sorry, non-type template argument of type %0 is not yet supported">;
def err_template_arg_not_convertible : Error<
"non-type template argument of type %0 cannot be converted to a value "
"of type %1">;

View File

@ -7238,6 +7238,8 @@ public:
NonTypeTemplateParmDecl *ConstrainedParameter,
SourceLocation EllipsisLoc);
bool RequireStructuralType(QualType T, SourceLocation Loc);
QualType CheckNonTypeTemplateParameterType(TypeSourceInfo *&TSI,
SourceLocation Loc);
QualType CheckNonTypeTemplateParameterType(QualType T, SourceLocation Loc);

View File

@ -100,7 +100,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
DefaultedDefaultConstructorIsConstexpr(true),
HasConstexprDefaultConstructor(false),
DefaultedDestructorIsConstexpr(true),
HasNonLiteralTypeFieldsOrBases(false),
HasNonLiteralTypeFieldsOrBases(false), StructuralIfLiteral(true),
UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0),
ImplicitCopyConstructorCanHaveConstParamForVBase(true),
ImplicitCopyConstructorCanHaveConstParamForNonVBase(true),
@ -258,9 +258,15 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
// C++1z [dcl.init.agg]p1:
// An aggregate is a class with [...] no private or protected base classes
if (Base->getAccessSpecifier() != AS_public)
if (Base->getAccessSpecifier() != AS_public) {
data().Aggregate = false;
// C++20 [temp.param]p7:
// A structural type is [...] a literal class type with [...] all base
// classes [...] public
data().StructuralIfLiteral = false;
}
// C++ [class.virtual]p1:
// A class that declares or inherits a virtual function is called a
// polymorphic class.
@ -536,6 +542,13 @@ void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
// array thereof, that class type shall have a constexpr destructor
if (!Subobj->hasConstexprDestructor())
data().DefaultedDestructorIsConstexpr = false;
// C++20 [temp.param]p7:
// A structural type is [...] a literal class type [for which] the types
// of all base classes and non-static data members are structural types or
// (possibly multi-dimensional) array thereof
if (!Subobj->data().StructuralIfLiteral)
data().StructuralIfLiteral = false;
}
bool CXXRecordDecl::hasConstexprDestructor() const {
@ -956,6 +969,11 @@ void CXXRecordDecl::addedMember(Decl *D) {
if (D->getAccess() == AS_private || D->getAccess() == AS_protected) {
data().Aggregate = false;
data().PlainOldData = false;
// C++20 [temp.param]p7:
// A structural type is [...] a literal class type [for which] all
// non-static data members are public
data().StructuralIfLiteral = false;
}
// Track whether this is the first field. We use this when checking
@ -980,9 +998,15 @@ void CXXRecordDecl::addedMember(Decl *D) {
}
// Keep track of the presence of mutable fields.
if (Field->isMutable())
if (Field->isMutable()) {
data().HasMutableFields = true;
// C++20 [temp.param]p7:
// A structural type is [...] a literal class type [for which] all
// non-static data members are public
data().StructuralIfLiteral = false;
}
// C++11 [class.union]p8, DR1460:
// If X is a union, a non-static data member of X that is not an anonymous
// union is a variant member of X.
@ -1315,6 +1339,14 @@ void CXXRecordDecl::addedMember(Decl *D) {
data().DefaultedCopyAssignmentIsDeleted = true;
data().DefaultedMoveAssignmentIsDeleted = true;
}
// C++20 [temp.param]p7:
// A structural type is [...] a literal class type [for which] the
// types of all non-static data members are structural types or
// (possibly multidimensional) array thereof
// We deal with class types elsewhere.
if (!T->isScalarType() && !T->isLValueReferenceType())
data().StructuralIfLiteral = false;
}
// C++14 [meta.unary.prop]p4:

View File

@ -2596,6 +2596,21 @@ bool Type::isLiteralType(const ASTContext &Ctx) const {
return false;
}
bool Type::isStructuralType() const {
// C++20 [temp.param]p6:
// A structural type is one of the following:
// -- a scalar type; or
if (isScalarType())
return true;
// -- an lvalue reference type; or
if (isLValueReferenceType())
return true;
// -- a literal class type [...under some conditions]
if (const CXXRecordDecl *RD = getAsCXXRecordDecl())
return RD->isStructural();
return false;
}
bool Type::isStandardLayoutType() const {
if (isDependentType())
return false;

View File

@ -1278,6 +1278,108 @@ QualType Sema::CheckNonTypeTemplateParameterType(TypeSourceInfo *&TSI,
return CheckNonTypeTemplateParameterType(TSI->getType(), Loc);
}
/// Require the given type to be a structural type, and diagnose if it is not.
///
/// \return \c true if an error was produced.
bool Sema::RequireStructuralType(QualType T, SourceLocation Loc) {
if (T->isDependentType())
return false;
if (RequireCompleteType(Loc, T, diag::err_template_nontype_parm_incomplete))
return true;
if (T->isStructuralType())
return false;
// Structural types are required to be object types or lvalue references.
if (T->isRValueReferenceType()) {
Diag(Loc, diag::err_template_nontype_parm_rvalue_ref) << T;
return true;
}
// Don't mention structural types in our diagnostic prior to C++20. Also,
// there's not much more we can say about non-scalar non-class types --
// because we can't see functions or arrays here, those can only be language
// extensions.
if (!getLangOpts().CPlusPlus20 ||
(!T->isScalarType() && !T->isRecordType())) {
Diag(Loc, diag::err_template_nontype_parm_bad_type) << T;
return true;
}
// Structural types are required to be literal types.
if (RequireLiteralType(Loc, T, diag::err_template_nontype_parm_not_literal))
return true;
Diag(Loc, diag::err_template_nontype_parm_not_structural) << T;
// Drill down into the reason why the class is non-structural.
while (const CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
// All members are required to be public and non-mutable, and can't be of
// rvalue reference type. Check these conditions first to prefer a "local"
// reason over a more distant one.
for (const FieldDecl *FD : RD->fields()) {
if (FD->getAccess() != AS_public) {
Diag(FD->getLocation(), diag::note_not_structural_non_public) << T << 0;
return true;
}
if (FD->isMutable()) {
Diag(FD->getLocation(), diag::note_not_structural_mutable_field) << T;
return true;
}
if (FD->getType()->isRValueReferenceType()) {
Diag(FD->getLocation(), diag::note_not_structural_rvalue_ref_field)
<< T;
return true;
}
}
// All bases are required to be public.
for (const auto &BaseSpec : RD->bases()) {
if (BaseSpec.getAccessSpecifier() != AS_public) {
Diag(BaseSpec.getBaseTypeLoc(), diag::note_not_structural_non_public)
<< T << 1;
return true;
}
}
// All subobjects are required to be of structural types.
SourceLocation SubLoc;
QualType SubType;
int Kind = -1;
for (const FieldDecl *FD : RD->fields()) {
QualType T = Context.getBaseElementType(FD->getType());
if (!T->isStructuralType()) {
SubLoc = FD->getLocation();
SubType = T;
Kind = 0;
break;
}
}
if (Kind == -1) {
for (const auto &BaseSpec : RD->bases()) {
QualType T = BaseSpec.getType();
if (!T->isStructuralType()) {
SubLoc = BaseSpec.getBaseTypeLoc();
SubType = T;
Kind = 1;
break;
}
}
}
assert(Kind != -1 && "couldn't find reason why type is not structural");
Diag(SubLoc, diag::note_not_structural_subobject)
<< T << Kind << SubType;
T = SubType;
RD = T->getAsCXXRecordDecl();
}
return true;
}
QualType Sema::CheckNonTypeTemplateParameterType(QualType T,
SourceLocation Loc) {
// We don't allow variably-modified types as the type of non-type template
@ -1297,13 +1399,13 @@ QualType Sema::CheckNonTypeTemplateParameterType(QualType T,
if (T->isIntegralOrEnumerationType() ||
// -- pointer to object or pointer to function,
T->isPointerType() ||
// -- reference to object or reference to function,
T->isReferenceType() ||
// -- lvalue reference to object or lvalue reference to function,
T->isLValueReferenceType() ||
// -- pointer to member,
T->isMemberPointerType() ||
// -- std::nullptr_t.
// -- std::nullptr_t, or
T->isNullPtrType() ||
// Allow use of auto in template parameter declarations.
// -- a type that contains a placeholder type.
T->isUndeducedType()) {
// C++ [temp.param]p5: The top-level cv-qualifiers on the template-parameter
// are ignored when determining its type.
@ -1327,10 +1429,20 @@ QualType Sema::CheckNonTypeTemplateParameterType(QualType T,
if (T->isDependentType())
return T.getUnqualifiedType();
Diag(Loc, diag::err_template_nontype_parm_bad_type)
<< T;
// C++20 [temp.param]p6:
// -- a structural type
if (RequireStructuralType(T, Loc))
return QualType();
return QualType();
if (!getLangOpts().CPlusPlus20) {
// FIXME: Consider allowing structural types as an extension in C++17. (In
// earlier language modes, the template argument evaluation rules are too
// inflexible.)
Diag(Loc, diag::err_template_nontype_parm_bad_structural_type) << T;
return QualType();
}
return T.getUnqualifiedType();
}
NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
@ -6866,6 +6978,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
return ExprError();
}
// -- a subobject
// FIXME: Until C++20
if (Value.hasLValuePath() && Value.getLValuePath().size() == 1 &&
VD && VD->getType()->isArrayType() &&
Value.getLValuePath()[0].getAsArrayIndex() == 0 &&
@ -6897,7 +7010,8 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
case APValue::Array:
case APValue::Struct:
case APValue::Union:
llvm_unreachable("invalid kind for template argument");
return Diag(StartLoc, diag::err_non_type_template_arg_unsupported)
<< ParamType;
}
return ArgResult.get();

View File

@ -1,15 +1,126 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17 -std=c++98 %s -Wno-c++11-extensions
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17 -std=c++17 %s
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx20 -std=c++20 %s
// C++98:
// A non-type template-parameter shall not be declared to have
// floating point, class, or void type.
struct A;
struct A; // expected-note {{forward declaration}}
template<double d> class X; // expected-error{{cannot have type}}
template<double d> class X; // cxx17-error{{cannot have type}}
template<double* pd> class Y; //OK
template<double& rd> class Z; //OK
template<A a> class X0; // expected-error{{cannot have type}}
template<A a> class X0; // expected-error{{has incomplete type 'A'}}
struct A {};
template<A a> class X0b; // cxx17-error{{cannot have type 'A' before C++20}}
typedef void VOID;
template<VOID a> class X01; // expected-error{{cannot have type}}
template<VOID a> class X01; // expected-error{{has incomplete type 'VOID'}}
// C++11 disallows rvalue references.
template<int &R> struct lval_ref;
template<int &&R> struct rval_ref; // expected-warning 0-1{{extension}} expected-error {{non-type template parameter has rvalue reference type 'int &&'}}
// C++20 requires a structural type. In addition to the above cases, this allows:
// arbitrary scalar types; we generally include complex types in that list
template<_Complex float ci> struct ComplexFloat; // cxx17-error {{cannot have type '_Complex float' before C++20}}
template<_Complex int ci> struct ComplexInt; // cxx17-error {{cannot have type '_Complex int' before C++20}}
template<_ExtInt(42) ei> struct ExtInt;
// atomic and vector types aren't scalar types
// FIXME: Consider permitting vector types here.
template<_Atomic float ci> struct AtomicFloat; // expected-error {{cannot have type '_Atomic(float)'}}
template<_Atomic int ci> struct AtomicInt; // expected-error {{cannot have type '_Atomic(int)'}}
typedef __attribute__((ext_vector_type(4))) int VI4;
typedef __attribute__((ext_vector_type(4))) float VF4;
template<VI4> struct VectorInt; // expected-error {{cannot have type 'VI4'}}
template<VF4> struct VectorFloat; // expected-error {{cannot have type 'VF4'}}
struct A2 {};
struct RRef {
int &&r; // cxx20-note 1+{{'RRef' is not a structural type because it has a non-static data member of rvalue reference type}}
};
// class types with all public members and bases, no mutable state, and no rvalue references.
struct B : A, public A2 {
int a;
private:
void f();
static int s;
public:
float g;
int &r = a;
void *p;
A2 a2;
RRef *ptr_to_bad;
RRef &ref_to_bad = *ptr_to_bad;
_Complex int ci;
_Complex float cf;
_ExtInt(42) ei;
};
template<B> struct ClassNTTP {}; // cxx17-error {{cannot have type 'B'}}
template<RRef> struct WithRRef {}; // cxx17-error {{cannot have type 'RRef'}}
// cxx20-error@-1 {{type 'RRef' of non-type template parameter is not a structural type}}
struct BadBase
: RRef {}; // cxx20-note {{'BadBase' is not a structural type because it has a base class of non-structural type 'RRef'}}
template<BadBase> struct WithBadBase {}; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}
struct BadField {
RRef r; // cxx20-note {{'BadField' is not a structural type because it has a non-static data member of non-structural type 'RRef'}}
};
template<BadField> struct WithBadField {}; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}
struct BadFieldArray {
RRef r[3][2]; // cxx20-note {{'BadFieldArray' is not a structural type because it has a non-static data member of non-structural type 'RRef'}}
};
template<BadFieldArray> struct WithBadFieldArray {}; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}
struct ProtectedBase
: protected A {}; // cxx20-note {{'ProtectedBase' is not a structural type because it has a base class that is not public}}
template<ProtectedBase> struct WithProtectedBase {}; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}
struct PrivateBase
: private A {}; // cxx20-note {{'PrivateBase' is not a structural type because it has a base class that is not public}}
template<PrivateBase> struct WithPrivateBase {}; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}
class Private2Base
: A {}; // cxx20-note {{'Private2Base' is not a structural type because it has a base class that is not public}}
template<Private2Base> struct WithPrivate2Base {}; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}
struct ProtectedField {
protected:
A r; // cxx20-note {{'ProtectedField' is not a structural type because it has a non-static data member that is not public}}
};
template<ProtectedField> struct WithProtectedField {}; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}
struct PrivateField {
private:
A r; // cxx20-note {{'PrivateField' is not a structural type because it has a non-static data member that is not public}}
};
template<PrivateField> struct WithPrivateField {}; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}
class Private2Field {
A r; // cxx20-note {{'Private2Field' is not a structural type because it has a non-static data member that is not public}}
};
template<Private2Field> struct WithPrivate2Field {}; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}
struct MutableField {
mutable int n; // cxx20-note {{'MutableField' is not a structural type because it has a mutable non-static data member}}
};
template<MutableField> struct WithMutableField {}; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}
template<typename T> struct BadExtType { T t; }; // cxx20-note 4{{has a non-static data member of non-structural type}}
template<BadExtType<_Atomic float> > struct AtomicFloatField; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}
template<BadExtType<_Atomic int> > struct AtomicInt; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}
template<BadExtType<VI4> > struct VectorInt; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}
template<BadExtType<VF4> > struct VectorFloat; // cxx17-error {{cannot have type}} cxx20-error {{is not a structural type}}

View File

@ -121,7 +121,7 @@ template<typename>
struct tmpl_impl {
};
template <template <typename> class tmpl, int &lvr, int &&rvr>
template <template <typename> class tmpl, int &lvr>
struct NN {
};
@ -129,16 +129,14 @@ struct NN {
// CHECK: [[NNV]] = distinct !DIGlobalVariable(name: "nn"
// CHECK-SAME: type: ![[NNT:[0-9]+]]
// CHECK: ![[NNT]] ={{.*}}!DICompositeType(tag: DW_TAG_structure_type, name: "NN<tmpl_impl, glb, glb>",
// CHECK: ![[NNT]] ={{.*}}!DICompositeType(tag: DW_TAG_structure_type, name: "NN<tmpl_impl, glb>",
// CHECK-SAME: templateParams: [[NNARGS:![0-9]*]]
// CHECK-SAME: identifier:
// CHECK: [[NNARGS]] = !{[[NNARG1:![0-9]*]], [[NNARG2:![0-9]*]], [[NNARG3:![0-9]*]]}
// CHECK: [[NNARGS]] = !{[[NNARG1:![0-9]*]], [[NNARG2:![0-9]*]]}
// CHECK: [[NNARG1]] = !DITemplateValueParameter(tag: DW_TAG_GNU_template_template_param, name: "tmpl", value: !"tmpl_impl")
// CHECK: [[NNARG2]] = !DITemplateValueParameter(name: "lvr", type: [[INTLVR:![0-9]*]], value: i32* @glb)
// CHECK: [[INTLVR]] = !DIDerivedType(tag: DW_TAG_reference_type, baseType: [[INT]]
// CHECK: [[NNARG3]] = !DITemplateValueParameter(name: "rvr", type: [[INTRVR:![0-9]*]], value: i32* @glb)
// CHECK: [[INTRVR]] = !DIDerivedType(tag: DW_TAG_rvalue_reference_type, baseType: [[INT]]
NN<tmpl_impl, glb, glb> nn;
NN<tmpl_impl, glb> nn;
// CHECK: ![[PADDINGATEND:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "PaddingAtEnd",
struct PaddingAtEnd {

View File

@ -3,7 +3,8 @@
template<class> class Foo {
template<class UBar // expected-error {{expected ';' after class}}
// expected-note@-1 {{'UBar' declared here}}
void foo1(); // expected-error {{a non-type template parameter cannot have type 'class UBar'}}
// expected-note@-2 {{forward declaration of 'UBar'}}
void foo1(); // expected-error {{non-type template parameter has incomplete type 'class UBar'}}
// expected-error@-1 {{expected ',' or '>' in template-parameter-list}}
// expected-error@-2 {{declaration does not declare anything}}
};

View File

@ -0,0 +1,44 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
// floating-point arguments
template<float> struct Float {};
using F1 = Float<1.0f>; // FIXME expected-error {{sorry}}
using F1 = Float<2.0f / 2>; // FIXME expected-error {{sorry}}
struct S { int n[3]; } s; // expected-note 1+{{here}}
int n; // expected-note 1+{{here}}
// pointers to subobjects
template<int *> struct IntPtr {};
using IPn = IntPtr<&n + 1>; // FIXME expected-error {{refers to subobject}}
using IPn = IntPtr<&n + 1>; // FIXME expected-error {{refers to subobject}}
using IP2 = IntPtr<&s.n[2]>; // FIXME expected-error {{refers to subobject}}
using IP2 = IntPtr<s.n + 2>; // FIXME expected-error {{refers to subobject}}
using IP3 = IntPtr<&s.n[3]>; // FIXME expected-error {{refers to subobject}}
using IP3 = IntPtr<s.n + 3>; // FIXME expected-error {{refers to subobject}}
template<int &> struct IntRef {};
using IPn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}}
using IPn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}}
using IP2 = IntRef<s.n[2]>; // FIXME expected-error {{refers to subobject}}
using IP2 = IntRef<*(s.n + 2)>; // FIXME expected-error {{refers to subobject}}
using IP3 = IntRef<s.n[3]>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}}
using IP3 = IntRef<*(s.n + 3)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}}
// classes
template<S> struct Struct {};
using S123 = Struct<S{1, 2, 3}>; // FIXME: expected-error {{sorry}}
using S123 = Struct<S{1, 2, 3}>; // FIXME: expected-error {{sorry}}
// miscellaneous scalar types
template<_Complex int> struct ComplexInt {};
using CI = ComplexInt<1 + 3i>; // FIXME: expected-error {{sorry}}
using CI = ComplexInt<1 + 3i>; // FIXME: expected-error {{sorry}}
template<_Complex float> struct ComplexFloat {};
using CF = ComplexFloat<1.0f + 3.0fi>; // FIXME: expected-error {{sorry}}
using CF = ComplexFloat<1.0f + 3.0fi>; // FIXME: expected-error {{sorry}}