non-type template parameters.
Create a unique TemplateParamObjectDecl instance for each such value,
representing the globally unique template parameter object to which the
template parameter refers.
No IR generation support yet; that will follow in a separate patch.
/// Mapping from GUIDs to the corresponding MSGuidDecl.
mutable llvm::FoldingSet<MSGuidDecl> MSGuidDecls;
+ /// Mapping from APValues to the corresponding TemplateParamObjects.
+ mutable llvm::FoldingSet<TemplateParamObjectDecl> TemplateParamObjectDecls;
+
/// A cache mapping a string value to a StringLiteral object with the same
/// value.
///
/// GUID value.
MSGuidDecl *getMSGuidDecl(MSGuidDeclParts Parts) const;
+ /// Return the template parameter object of the given type with the given
+ /// value.
+ TemplateParamObjectDecl *getTemplateParamObjectDecl(QualType T,
+ const APValue &V) const;
+
/// Parses the target attributes passed in, and returns only the ones that are
/// valid feature names.
ParsedTargetAttr filterFunctionTargetAttrs(const TargetAttr *TD) const;
static bool classofKind(Kind K) { return K == VarTemplate; }
};
-// \brief Declaration of a C++2a concept.
+/// Declaration of a C++2a concept.
class ConceptDecl : public TemplateDecl, public Mergeable<ConceptDecl> {
protected:
Expr *ConstraintExpr;
return isa<TemplateTypeParmDecl>(getTemplateParameters()->getParam(0));
}
+ ConceptDecl *getCanonicalDecl() override { return getFirstDecl(); }
+ const ConceptDecl *getCanonicalDecl() const { return getFirstDecl(); }
+
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == Concept; }
friend class ASTDeclWriter;
};
+/// A template parameter object.
+///
+/// Template parameter objects represent values of class type used as template
+/// arguments. There is one template parameter object for each such distinct
+/// value used as a template argument across the program.
+///
+/// \code
+/// struct A { int x, y; };
+/// template<A> struct S;
+/// S<A{1, 2}> s1;
+/// S<A{1, 2}> s2; // same type, argument is same TemplateParamObjectDecl.
+/// \endcode
+class TemplateParamObjectDecl : public ValueDecl,
+ public Mergeable<TemplateParamObjectDecl>,
+ public llvm::FoldingSetNode {
+private:
+ /// The value of this template parameter object.
+ APValue Value;
+
+ TemplateParamObjectDecl(DeclContext *DC, QualType T, const APValue &V)
+ : ValueDecl(TemplateParamObject, DC, SourceLocation(), DeclarationName(),
+ T),
+ Value(V) {}
+
+ static TemplateParamObjectDecl *Create(const ASTContext &C, QualType T,
+ const APValue &V);
+ static TemplateParamObjectDecl *CreateDeserialized(ASTContext &C,
+ unsigned ID);
+
+ /// Only ASTContext::getTemplateParamObjectDecl and deserialization
+ /// create these.
+ friend class ASTContext;
+ friend class ASTReader;
+ friend class ASTDeclReader;
+
+public:
+ /// Print this template parameter object in a human-readable format.
+ void printName(llvm::raw_ostream &OS) const override;
+
+ /// Print this object as an equivalent expression.
+ void printAsExpr(llvm::raw_ostream &OS) const;
+
+ /// Print this object as an initializer suitable for a variable of the
+ /// object's type.
+ void printAsInit(llvm::raw_ostream &OS) const;
+
+ const APValue &getValue() const { return Value; }
+
+ static void Profile(llvm::FoldingSetNodeID &ID, QualType T,
+ const APValue &V) {
+ ID.AddPointer(T.getCanonicalType().getAsOpaquePtr());
+ V.profile(ID);
+ }
+ void Profile(llvm::FoldingSetNodeID &ID) {
+ Profile(ID, getType(), getValue());
+ }
+
+ TemplateParamObjectDecl *getCanonicalDecl() override {
+ return getFirstDecl();
+ }
+ const TemplateParamObjectDecl *getCanonicalDecl() const {
+ return getFirstDecl();
+ }
+
+ static bool classof(const Decl *D) { return classofKind(D->getKind()); }
+ static bool classofKind(Kind K) { return K == TemplateParamObject; }
+};
+
inline NamedDecl *getAsNamedDecl(TemplateParameter P) {
if (auto *PD = P.dyn_cast<TemplateTypeParmDecl *>())
return PD;
DEF_TRAVERSE_DECL(MSGuidDecl, {})
+DEF_TRAVERSE_DECL(TemplateParamObjectDecl, {})
+
DEF_TRAVERSE_DECL(FieldDecl, {
TRY_TO(TraverseDeclaratorHelper(D));
if (D->isBitField())
def OMPDeclareReduction : DeclNode<Value>, DeclContext;
def OMPDeclareMapper : DeclNode<Value>, DeclContext;
def MSGuid : DeclNode<Value>;
+ def TemplateParamObject : DeclNode<Value>;
def Declarator : DeclNode<Value, "declarators", 1>;
def Field : DeclNode<Declarator, "non-static data members">;
def ObjCIvar : DeclNode<Field>;
// C++ initialization
def err_init_conversion_failed : Error<
- "cannot initialize %select{a variable|a parameter|return object|"
- "statement expression result|an "
+ "cannot initialize %select{a variable|a parameter|template parameter|"
+ "return object|statement expression result|an "
"exception object|a member subobject|an array element|a new value|a value|a "
"base class|a constructor delegation|a vector element|a block element|a "
"block element|a complex element|a lambda capture|a compound literal "
"unsequenced modification and access to %0">, InGroup<Unsequenced>;
def select_initialized_entity_kind : TextSubstitution<
- "%select{copying variable|copying parameter|"
+ "%select{copying variable|copying parameter|initializing template parameter|"
"returning object|initializing statement expression result|"
"throwing object|copying member subobject|copying array element|"
"allocating object|copying temporary|initializing base subobject|"
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_cxx17_compat_template_nontype_parm_type : Warning<
+ "non-type template parameter of type %0 is incompatible with "
+ "C++ standards before C++20">,
+ DefaultIgnore, InGroup<CXXPre20Compat>;
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">,
/// The entity being initialized is a function parameter.
EK_Parameter,
+ /// The entity being initialized is a non-type template parameter.
+ EK_TemplateParameter,
+
/// The entity being initialized is the result of a function call.
EK_Result,
};
union {
- /// When Kind == EK_Variable, EK_Member or EK_Binding, the variable.
+ /// When Kind == EK_Variable, EK_Member, EK_Binding, or
+ /// EK_TemplateParameter, the variable, binding, or template parameter.
VD Variable;
/// When Kind == EK_RelatedResult, the ObjectiveC method where
return Entity;
}
+ /// Create the initialization entity for a template parameter.
+ static InitializedEntity
+ InitializeTemplateParameter(QualType T, NonTypeTemplateParmDecl *Param) {
+ InitializedEntity Entity;
+ Entity.Kind = EK_TemplateParameter;
+ Entity.Type = T;
+ Entity.Parent = nullptr;
+ Entity.Variable = {Param, false, false};
+ return Entity;
+ }
+
/// Create the initialization entity for the result of a function.
static InitializedEntity InitializeResult(SourceLocation ReturnLoc,
QualType Type, bool NRVO) {
getKind() == EK_Parameter_CF_Audited);
}
+ bool isParamOrTemplateParamKind() const {
+ return isParameterKind() || getKind() == EK_TemplateParameter;
+ }
+
/// Determine whether this initialization consumes the
/// parameter.
bool isParameterConsumed() const {
ExprResult CheckConvertedConstantExpression(Expr *From, QualType T,
llvm::APSInt &Value, CCEKind CCE);
ExprResult CheckConvertedConstantExpression(Expr *From, QualType T,
- APValue &Value, CCEKind CCE);
+ APValue &Value, CCEKind CCE,
+ NamedDecl *Dest = nullptr);
/// Abstract base class used to perform a contextual implicit
/// conversion from an expression to any type passing a filter.
/// A MSGuidDecl record.
DECL_MS_GUID,
+ /// A TemplateParamObjectDecl record.
+ DECL_TEMPLATE_PARAM_OBJECT,
+
/// A VarDecl record.
DECL_VAR,
Arg = TemplateArgument(ArgType);
} else if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
+ QualType T =
+ NTTP->getType().getNonPackExpansionType().getNonLValueExprType(*this);
+ // For class NTTPs, ensure we include the 'const' so the type matches that
+ // of a real template argument.
+ // FIXME: It would be more faithful to model this as something like an
+ // lvalue-to-rvalue conversion applied to a const-qualified lvalue.
+ if (T->isRecordType())
+ T.addConst();
Expr *E = new (*this) DeclRefExpr(
- *this, NTTP, /*enclosing*/ false,
- NTTP->getType().getNonPackExpansionType().getNonLValueExprType(*this),
+ *this, NTTP, /*enclosing*/ false, T,
Expr::getValueKindForType(NTTP->getType()), NTTP->getLocation());
if (NTTP->isParameterPack())
return New;
}
+TemplateParamObjectDecl *
+ASTContext::getTemplateParamObjectDecl(QualType T, const APValue &V) const {
+ assert(T->isRecordType() && "template param object of unexpected type");
+
+ // C++ [temp.param]p8:
+ // [...] a static storage duration object of type 'const T' [...]
+ T.addConst();
+
+ llvm::FoldingSetNodeID ID;
+ TemplateParamObjectDecl::Profile(ID, T, V);
+
+ void *InsertPos;
+ if (TemplateParamObjectDecl *Existing =
+ TemplateParamObjectDecls.FindNodeOrInsertPos(ID, InsertPos))
+ return Existing;
+
+ TemplateParamObjectDecl *New = TemplateParamObjectDecl::Create(*this, T, V);
+ TemplateParamObjectDecls.InsertNode(New, InsertPos);
+ return New;
+}
+
bool ASTContext::AtomicUsesUnsupportedLibcall(const AtomicExpr *E) const {
const llvm::Triple &T = getTargetInfo().getTriple();
if (!T.isOSDarwin())
if (VD) {
if (AddressOf)
OS << "&";
- OS << VD->getName();
+ else if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(VD)) {
+ // FIXME: Diffing the APValue would be neat.
+ // FIXME: Suppress this and use the full name of the declaration if the
+ // parameter is a pointer or reference.
+ TPO->printAsInit(OS);
+ return;
+ }
+ VD->printName(OS);
return;
}
case ExternCContext:
case Decomposition:
case MSGuid:
+ case TemplateParamObject:
case UsingDirective:
case BuiltinTemplate:
OS << ">";
}
}
+
+TemplateParamObjectDecl *TemplateParamObjectDecl::Create(const ASTContext &C,
+ QualType T,
+ const APValue &V) {
+ DeclContext *DC = C.getTranslationUnitDecl();
+ auto *TPOD = new (C, DC) TemplateParamObjectDecl(DC, T, V);
+ C.addDestruction(&TPOD->Value);
+ return TPOD;
+}
+
+TemplateParamObjectDecl *
+TemplateParamObjectDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
+ auto *TPOD = new (C, ID) TemplateParamObjectDecl(nullptr, QualType(), APValue());
+ C.addDestruction(&TPOD->Value);
+ return TPOD;
+}
+
+void TemplateParamObjectDecl::printName(llvm::raw_ostream &OS) const {
+ OS << "<template param ";
+ printAsExpr(OS);
+ OS << ">";
+}
+
+void TemplateParamObjectDecl::printAsExpr(llvm::raw_ostream &OS) const {
+ const ASTContext &Ctx = getASTContext();
+ getType().getUnqualifiedType().print(OS, Ctx.getPrintingPolicy());
+ printAsInit(OS);
+}
+
+void TemplateParamObjectDecl::printAsInit(llvm::raw_ostream &OS) const {
+ const ASTContext &Ctx = getASTContext();
+ getValue().printPretty(OS, Ctx, getType());
+}
bool islvalue;
if (const auto *NTTParm = dyn_cast<NonTypeTemplateParmDecl>(D))
- islvalue = NTTParm->getType()->isReferenceType();
+ islvalue = NTTParm->getType()->isReferenceType() ||
+ NTTParm->getType()->isRecordType();
else
islvalue = isa<VarDecl>(D) || isa<FieldDecl>(D) ||
isa<IndirectFieldDecl>(D) ||
isa<BindingDecl>(D) ||
isa<MSGuidDecl>(D) ||
+ isa<TemplateParamObjectDecl>(D) ||
(Ctx.getLangOpts().CPlusPlus &&
(isa<FunctionDecl>(D) || isa<MSPropertyDecl>(D) ||
isa<FunctionTemplateDecl>(D)));
// ... the address of an object with static storage duration,
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
return VD->hasGlobalStorage();
+ if (isa<TemplateParamObjectDecl>(D))
+ return true;
// ... the address of a function,
// ... the address of a GUID [MS extension],
return isa<FunctionDecl>(D) || isa<MSGuidDecl>(D);
return CompleteObject(LVal.Base, &V, GD->getType());
}
+ // Allow reading from template parameter objects.
+ if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(D)) {
+ if (isModification(AK)) {
+ Info.FFDiag(E, diag::note_constexpr_modify_global);
+ return CompleteObject();
+ }
+ return CompleteObject(LVal.Base, const_cast<APValue *>(&TPO->getValue()),
+ TPO->getType());
+ }
+
// In C++98, const, non-volatile integers initialized with ICEs are ICEs.
// In C++11, constexpr, non-volatile variables initialized with constant
// expressions are constant expressions too. Inside constexpr functions,
}
bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(E->getDecl()))
- return Success(FD);
- if (const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()))
+ const NamedDecl *D = E->getDecl();
+ if (isa<FunctionDecl, MSGuidDecl, TemplateParamObjectDecl>(D))
+ return Success(cast<ValueDecl>(D));
+ if (const VarDecl *VD = dyn_cast<VarDecl>(D))
return VisitVarDecl(E, VD);
- if (const BindingDecl *BD = dyn_cast<BindingDecl>(E->getDecl()))
+ if (const BindingDecl *BD = dyn_cast<BindingDecl>(D))
return Visit(BD->getBinding());
- if (const MSGuidDecl *GD = dyn_cast<MSGuidDecl>(E->getDecl()))
- return Success(GD);
return Error(E);
}
mangleName(GuidD);
else if (const BindingDecl *BD = dyn_cast<BindingDecl>(GD.getDecl()))
mangleName(BD);
- else
+ else if (isa<TemplateParamObjectDecl>(GD.getDecl())) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle template parameter objects yet");
+ Diags.Report(SourceLocation(), DiagID);
+ } else
llvm_unreachable("unexpected kind of global decl");
}
// MSVC appears to mangle GUIDs as if they were variables of type
// 'const struct __s_GUID'.
Out << "3U__s_GUID@@B";
- else
+ else if (isa<TemplateParamObjectDecl>(D)) {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot mangle template parameter objects yet");
+ Diags.Report(SourceLocation(), DiagID);
+ } else
llvm_unreachable("Tried to mangle unexpected NamedDecl!");
}
OCED->getInit()->IgnoreImpCasts()->printPretty(OS, nullptr, Policy);
return;
}
+ if (const auto *TPOD = dyn_cast<TemplateParamObjectDecl>(Node->getDecl())) {
+ TPOD->printAsExpr(OS);
+ return;
+ }
if (NestedNameSpecifier *Qualifier = Node->getQualifier())
Qualifier->print(OS, Policy);
if (Node->hasTemplateKeyword())
case Declaration: {
NamedDecl *ND = getAsDecl();
+ if (getParamTypeForDecl()->isRecordType()) {
+ if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(ND)) {
+ // FIXME: Include the type if it's not obvious from the context.
+ TPO->printAsInit(Out);
+ break;
+ }
+ }
if (!getParamTypeForDecl()->isReferenceType())
Out << '&';
ND->printQualifiedName(Out);
case Decl::Label: // __label__ x;
case Decl::Import:
case Decl::MSGuid: // __declspec(uuid("..."))
+ case Decl::TemplateParamObject:
case Decl::OMPThreadPrivate:
case Decl::OMPAllocate:
case Decl::OMPCapturedExpr:
return MakeAddrLValue(CGM.GetAddrOfMSGuidDecl(GD), T,
AlignmentSource::Decl);
+ if (const auto *TPO = dyn_cast<TemplateParamObjectDecl>(ND))
+ return MakeAddrLValue(CGM.GetAddrOfTemplateParamObject(TPO), T,
+ AlignmentSource::Decl);
+
llvm_unreachable("Unhandled DeclRefExpr");
}
if (auto *GD = dyn_cast<MSGuidDecl>(D))
return CGM.GetAddrOfMSGuidDecl(GD);
+ if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(D))
+ return CGM.GetAddrOfTemplateParamObject(TPO);
+
return nullptr;
}
return ConstantAddress(Addr, Alignment);
}
+ConstantAddress CodeGenModule::GetAddrOfTemplateParamObject(
+ const TemplateParamObjectDecl *TPO) {
+ ErrorUnsupported(TPO, "template parameter object");
+ return ConstantAddress::invalid();
+}
+
ConstantAddress CodeGenModule::GetWeakRefReference(const ValueDecl *VD) {
const AliasAttr *AA = VD->getAttr<AliasAttr>();
assert(AA && "No alias?");
/// Get the address of a GUID.
ConstantAddress GetAddrOfMSGuidDecl(const MSGuidDecl *GD);
+ /// Get the address of a template parameter object.
+ ConstantAddress
+ GetAddrOfTemplateParamObject(const TemplateParamObjectDecl *TPO);
+
/// Get the address of the thunk for the given global decl.
llvm::Constant *GetAddrOfThunk(StringRef Name, llvm::Type *FnTy,
GlobalDecl GD);
break;
}
+ // [expr.prim.id.unqual]p2:
+ // If the entity is a template parameter object for a template
+ // parameter of type T, the type of the expression is const T.
+ // [...] The expression is an lvalue if the entity is a [...] template
+ // parameter object.
+ if (type->isRecordType()) {
+ type = type.getUnqualifiedType().withConst();
+ valueKind = VK_LValue;
+ break;
+ }
+
// For non-references, we need to strip qualifiers just in case
// the template parameter was declared as 'const int' or whatever.
valueKind = VK_RValue;
case Decl::MSProperty:
case Decl::MSGuid:
- // FIXME: Should MSGuidDecl be subject to capture in OpenMP,
- // or duplicated between host and device?
+ case Decl::TemplateParamObject:
+ // FIXME: Should MSGuidDecl and template parameter objects be subject to
+ // capture in OpenMP, or duplicated between host and device?
valueKind = VK_LValue;
break;
case InitializedEntity::EK_ArrayElement:
case InitializedEntity::EK_Parameter:
case InitializedEntity::EK_Parameter_CF_Audited:
+ case InitializedEntity::EK_TemplateParameter:
case InitializedEntity::EK_Result:
// Extra braces here are suspicious.
DiagID = diag::warn_braces_around_init;
case EK_Variable:
case EK_Member:
case EK_Binding:
+ case EK_TemplateParameter:
return Variable.VariableOrMember->getDeclName();
case EK_LambdaCapture:
case EK_Variable:
case EK_Member:
case EK_Binding:
+ case EK_TemplateParameter:
return Variable.VariableOrMember;
case EK_Parameter:
case EK_Variable:
case EK_Parameter:
case EK_Parameter_CF_Audited:
+ case EK_TemplateParameter:
case EK_Member:
case EK_Binding:
case EK_New:
case EK_Parameter: OS << "Parameter"; break;
case EK_Parameter_CF_Audited: OS << "CF audited function Parameter";
break;
+ case EK_TemplateParameter: OS << "TemplateParameter"; break;
case EK_Result: OS << "Result"; break;
case EK_StmtExprResult: OS << "StmtExprResult"; break;
case EK_Exception: OS << "Exception"; break;
// FIXME: Can we tell apart casting vs. converting?
return Sema::AA_Casting;
+ case InitializedEntity::EK_TemplateParameter:
+ // This is really initialization, but refer to it as conversion for
+ // consistency with CheckConvertedConstantExpression.
+ return Sema::AA_Converting;
+
case InitializedEntity::EK_Member:
case InitializedEntity::EK_Binding:
case InitializedEntity::EK_ArrayElement:
case InitializedEntity::EK_LambdaToBlockConversionBlockElement:
case InitializedEntity::EK_LambdaCapture:
case InitializedEntity::EK_CompoundLiteralInit:
+ case InitializedEntity::EK_TemplateParameter:
return false;
case InitializedEntity::EK_Parameter:
case InitializedEntity::EK_Variable:
case InitializedEntity::EK_Parameter:
case InitializedEntity::EK_Parameter_CF_Audited:
+ case InitializedEntity::EK_TemplateParameter:
case InitializedEntity::EK_Temporary:
case InitializedEntity::EK_ArrayElement:
case InitializedEntity::EK_Exception:
case InitializedEntity::EK_Member:
case InitializedEntity::EK_Parameter:
case InitializedEntity::EK_Parameter_CF_Audited:
+ case InitializedEntity::EK_TemplateParameter:
case InitializedEntity::EK_Temporary:
case InitializedEntity::EK_New:
case InitializedEntity::EK_Base:
void InitializationSequence::PrintInitLocationNote(Sema &S,
const InitializedEntity &Entity) {
- if (Entity.isParameterKind() && Entity.getDecl()) {
+ if (Entity.isParamOrTemplateParamKind() && Entity.getDecl()) {
if (Entity.getDecl()->getLocation().isInvalid())
return;
// the call.
return {nullptr, LK_FullExpression};
+ case InitializedEntity::EK_TemplateParameter:
+ // FIXME: This will always be ill-formed; should we eagerly diagnose it here?
+ return {nullptr, LK_FullExpression};
+
case InitializedEntity::EK_Result:
// -- The lifetime of a temporary bound to the returned value in a
// function return statement is not extended; the temporary is
if (S.getLangOpts().CPlusPlus11 && Entity.getType()->isReferenceType() &&
Args.size() == 1 && isa<InitListExpr>(Args[0]) &&
- !Entity.isParameterKind()) {
+ !Entity.isParamOrTemplateParamKind()) {
// Produce a C++98 compatibility warning if we are initializing a reference
// from an initializer list. For parameters, we produce a better warning
// elsewhere.
static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
QualType T, APValue &Value,
Sema::CCEKind CCE,
- bool RequireInt) {
+ bool RequireInt,
+ NamedDecl *Dest) {
assert(S.getLangOpts().CPlusPlus11 &&
"converted constant expression outside C++11");
SCS = &ICS.Standard;
break;
case ImplicitConversionSequence::UserDefinedConversion:
- // We are converting to a non-class type, so the Before sequence
- // must be trivial.
- SCS = &ICS.UserDefined.After;
+ if (T->isRecordType())
+ SCS = &ICS.UserDefined.Before;
+ else
+ SCS = &ICS.UserDefined.After;
break;
case ImplicitConversionSequence::AmbiguousConversion:
case ImplicitConversionSequence::BadConversion:
<< From->getType() << From->getSourceRange() << T;
}
- ExprResult Result =
- S.PerformImplicitConversion(From, T, ICS, Sema::AA_Converting);
+ // Usually we can simply apply the ImplicitConversionSequence we formed
+ // earlier, but that's not guaranteed to work when initializing an object of
+ // class type.
+ ExprResult Result;
+ if (T->isRecordType()) {
+ assert(CCE == Sema::CCEK_TemplateArg &&
+ "unexpected class type converted constant expr");
+ Result = S.PerformCopyInitialization(
+ InitializedEntity::InitializeTemplateParameter(
+ T, cast<NonTypeTemplateParmDecl>(Dest)),
+ SourceLocation(), From);
+ } else {
+ Result = S.PerformImplicitConversion(From, T, ICS, Sema::AA_Converting);
+ }
if (Result.isInvalid())
return Result;
}
ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T,
- APValue &Value, CCEKind CCE) {
- return ::CheckConvertedConstantExpression(*this, From, T, Value, CCE, false);
+ APValue &Value, CCEKind CCE,
+ NamedDecl *Dest) {
+ return ::CheckConvertedConstantExpression(*this, From, T, Value, CCE, false,
+ Dest);
}
ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T,
assert(T->isIntegralOrEnumerationType() && "unexpected converted const type");
APValue V;
- auto R = ::CheckConvertedConstantExpression(*this, From, T, V, CCE, true);
+ auto R = ::CheckConvertedConstantExpression(*this, From, T, V, CCE, true,
+ /*Dest=*/nullptr);
if (!R.isInvalid() && !R.get()->isValueDependent())
Value = V.getInt();
return R;
return QualType();
}
+ Diag(Loc, diag::warn_cxx17_compat_template_nontype_parm_type) << T;
return T.getUnqualifiedType();
}
assert(!ParamType.hasQualifiers() &&
"non-type template parameter type cannot be qualified");
+ // FIXME: When Param is a reference, should we check that Arg is an lvalue?
if (CTAK == CTAK_Deduced &&
- !Context.hasSameType(ParamType.getNonLValueExprType(Context),
- Arg->getType())) {
+ (ParamType->isReferenceType()
+ ? !Context.hasSameType(ParamType.getNonReferenceType(),
+ Arg->getType())
+ : !Context.hasSameUnqualifiedType(ParamType, Arg->getType()))) {
// FIXME: If either type is dependent, we skip the check. This isn't
// correct, since during deduction we're supposed to have replaced each
// template parameter with some unique (non-dependent) placeholder.
*this, Sema::ExpressionEvaluationContext::ConstantEvaluated);
if (getLangOpts().CPlusPlus17) {
+ QualType CanonParamType = Context.getCanonicalType(ParamType);
+
+ // Avoid making a copy when initializing a template parameter of class type
+ // from a template parameter object of the same type. This is going beyond
+ // the standard, but is required for soundness: in
+ // template<A a> struct X { X *p; X<a> *q; };
+ // ... we need p and q to have the same type.
+ //
+ // Similarly, don't inject a call to a copy constructor when initializing
+ // from a template parameter of the same type.
+ Expr *InnerArg = Arg;
+ while (auto *SNTTP = dyn_cast<SubstNonTypeTemplateParmExpr>(InnerArg))
+ InnerArg = SNTTP->getReplacement();
+ if (ParamType->isRecordType() && isa<DeclRefExpr>(InnerArg) &&
+ Context.hasSameUnqualifiedType(ParamType, InnerArg->getType())) {
+ NamedDecl *ND = cast<DeclRefExpr>(InnerArg)->getDecl();
+ if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(ND)) {
+ Converted = TemplateArgument(TPO, CanonParamType);
+ return Arg;
+ }
+ if (isa<NonTypeTemplateParmDecl>(ND)) {
+ Converted = TemplateArgument(Arg);
+ return Arg;
+ }
+ }
+
// C++17 [temp.arg.nontype]p1:
// A template-argument for a non-type template parameter shall be
// a converted constant expression of the type of the template-parameter.
APValue Value;
ExprResult ArgResult = CheckConvertedConstantExpression(
- Arg, ParamType, Value, CCEK_TemplateArg);
+ Arg, ParamType, Value, CCEK_TemplateArg, Param);
if (ArgResult.isInvalid())
return ExprError();
return ArgResult;
}
- QualType CanonParamType = Context.getCanonicalType(ParamType);
-
// Convert the APValue to a TemplateArgument.
switch (Value.getKind()) {
case APValue::None:
: TemplateArgument(CanonParamType, /*isNullPtr*/true);
break;
}
+ case APValue::Struct:
+ case APValue::Union:
+ // Get or create the corresponding template parameter object.
+ Converted = TemplateArgument(
+ Context.getTemplateParamObjectDecl(CanonParamType, Value),
+ CanonParamType);
+ break;
case APValue::AddrLabelDiff:
return Diag(StartLoc, diag::err_non_type_template_arg_addr_label_diff);
case APValue::FixedPoint:
case APValue::ComplexFloat:
case APValue::Vector:
case APValue::Array:
- case APValue::Struct:
- case APValue::Union:
return Diag(StartLoc, diag::err_non_type_template_arg_unsupported)
<< ParamType;
}
RefExpr = CreateBuiltinUnaryOp(Loc, UO_AddrOf, RefExpr.get());
if (RefExpr.isInvalid())
return ExprError();
+ } else if (ParamType->isRecordType()) {
+ assert(isa<TemplateParamObjectDecl>(VD) &&
+ "arg for class template param not a template parameter object");
+ // No conversions apply in this case.
+ return RefExpr;
} else {
assert(ParamType->isReferenceType() &&
"unexpected type for decl template argument");
/// If the given expression is of a form that permits the deduction
/// of a non-type template parameter, return the declaration of that
/// non-type template parameter.
-static NonTypeTemplateParmDecl *
-getDeducedParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) {
+static const NonTypeTemplateParmDecl *
+getDeducedParameterFromExpr(const Expr *E, unsigned Depth) {
// If we are within an alias template, the expression may have undergone
// any number of parameter substitutions already.
while (true) {
- if (ImplicitCastExpr *IC = dyn_cast<ImplicitCastExpr>(E))
+ if (const auto *IC = dyn_cast<ImplicitCastExpr>(E))
E = IC->getSubExpr();
- else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(E))
+ else if (const auto *CE = dyn_cast<ConstantExpr>(E))
E = CE->getSubExpr();
- else if (SubstNonTypeTemplateParmExpr *Subst =
- dyn_cast<SubstNonTypeTemplateParmExpr>(E))
+ else if (const auto *Subst = dyn_cast<SubstNonTypeTemplateParmExpr>(E))
E = Subst->getReplacement();
- else
+ else if (const auto *CCE = dyn_cast<CXXConstructExpr>(E)) {
+ // Look through implicit copy construction from an lvalue of the same type.
+ if (CCE->getParenOrBraceRange().isValid())
+ break;
+ // Note, there could be default arguments.
+ assert(CCE->getNumArgs() >= 1 && "implicit construct expr should have 1 arg");
+ E = CCE->getArg(0);
+ } else
break;
}
- if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
- if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl()))
- if (NTTP->getDepth() == Info.getDeducedDepth())
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl()))
+ if (NTTP->getDepth() == Depth)
return NTTP;
return nullptr;
}
+static const NonTypeTemplateParmDecl *
+getDeducedParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) {
+ return getDeducedParameterFromExpr(E, Info.getDeducedDepth());
+}
+
/// Determine whether two declaration pointers refer to the same
/// declaration.
static bool isSameDeclaration(Decl *X, Decl *Y) {
/// deduction is funneled through here.
static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument(
Sema &S, TemplateParameterList *TemplateParams,
- NonTypeTemplateParmDecl *NTTP, const DeducedTemplateArgument &NewDeduced,
+ const NonTypeTemplateParmDecl *NTTP, const DeducedTemplateArgument &NewDeduced,
QualType ValueType, TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced) {
assert(NTTP->getDepth() == Info.getDeducedDepth() &&
DeducedTemplateArgument Result = checkDeducedTemplateArguments(
S.Context, Deduced[NTTP->getIndex()], NewDeduced);
if (Result.isNull()) {
- Info.Param = NTTP;
+ Info.Param = const_cast<NonTypeTemplateParmDecl*>(NTTP);
Info.FirstArg = Deduced[NTTP->getIndex()];
Info.SecondArg = NewDeduced;
return Sema::TDK_Inconsistent;
// type from an argument (of non-reference type) should be performed.
// For now, we just remove reference types from both sides and let
// the final check for matching types sort out the mess.
+ ValueType = ValueType.getNonReferenceType();
+ if (ParamType->isReferenceType())
+ ParamType = ParamType.getNonReferenceType();
+ else
+ // Top-level cv-qualifiers are irrelevant for a non-reference type.
+ ValueType = ValueType.getUnqualifiedType();
+
return DeduceTemplateArgumentsByTypeMatch(
- S, TemplateParams, ParamType.getNonReferenceType(),
- ValueType.getNonReferenceType(), Info, Deduced, TDF_SkipNonDependent,
- /*PartialOrdering=*/false,
+ S, TemplateParams, ParamType, ValueType, Info, Deduced,
+ TDF_SkipNonDependent, /*PartialOrdering=*/false,
/*ArrayBound=*/NewDeduced.wasDeducedFromArrayBound());
}
/// from the given integral constant.
static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument(
Sema &S, TemplateParameterList *TemplateParams,
- NonTypeTemplateParmDecl *NTTP, const llvm::APSInt &Value,
+ const NonTypeTemplateParmDecl *NTTP, const llvm::APSInt &Value,
QualType ValueType, bool DeducedFromArrayBound, TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced) {
return DeduceNonTypeTemplateArgument(
/// from the given null pointer template argument type.
static Sema::TemplateDeductionResult DeduceNullPtrTemplateArgument(
Sema &S, TemplateParameterList *TemplateParams,
- NonTypeTemplateParmDecl *NTTP, QualType NullPtrType,
+ const NonTypeTemplateParmDecl *NTTP, QualType NullPtrType,
TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced) {
Expr *Value =
/// \returns true if deduction succeeded, false otherwise.
static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument(
Sema &S, TemplateParameterList *TemplateParams,
- NonTypeTemplateParmDecl *NTTP, Expr *Value, TemplateDeductionInfo &Info,
+ const NonTypeTemplateParmDecl *NTTP, Expr *Value, TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced) {
return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP,
DeducedTemplateArgument(Value),
/// \returns true if deduction succeeded, false otherwise.
static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument(
Sema &S, TemplateParameterList *TemplateParams,
- NonTypeTemplateParmDecl *NTTP, ValueDecl *D, QualType T,
+ const NonTypeTemplateParmDecl *NTTP, ValueDecl *D, QualType T,
TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced) {
D = D ? cast<ValueDecl>(D->getCanonicalDecl()) : nullptr;
return Result;
// Determine the array bound is something we can deduce.
- NonTypeTemplateParmDecl *NTTP
+ const NonTypeTemplateParmDecl *NTTP
= getDeducedParameterFromExpr(Info, DependentArrayParm->getSizeExpr());
if (!NTTP)
return Sema::TDK_Success;
// deducing through the noexcept-specifier if it's part of the canonical
// type. libstdc++ relies on this.
Expr *NoexceptExpr = FunctionProtoParam->getNoexceptExpr();
- if (NonTypeTemplateParmDecl *NTTP =
+ if (const NonTypeTemplateParmDecl *NTTP =
NoexceptExpr ? getDeducedParameterFromExpr(Info, NoexceptExpr)
: nullptr) {
assert(NTTP->getDepth() == Info.getDeducedDepth() &&
return Result;
// Perform deduction on the vector size, if we can.
- NonTypeTemplateParmDecl *NTTP =
+ const NonTypeTemplateParmDecl *NTTP =
getDeducedParameterFromExpr(Info, VectorParam->getSizeExpr());
if (!NTTP)
return Sema::TDK_Success;
return Result;
// Perform deduction on the vector size, if we can.
- NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(
+ const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(
Info, VectorParam->getSizeExpr());
if (!NTTP)
return Sema::TDK_Success;
return Result;
// Perform deduction on the vector size, if we can.
- NonTypeTemplateParmDecl *NTTP
- = getDeducedParameterFromExpr(Info, VectorParam->getSizeExpr());
+ const NonTypeTemplateParmDecl *NTTP =
+ getDeducedParameterFromExpr(Info, VectorParam->getSizeExpr());
if (!NTTP)
return Sema::TDK_Success;
return Result;
// Perform deduction on the vector size, if we can.
- NonTypeTemplateParmDecl *NTTP
- = getDeducedParameterFromExpr(Info, VectorParam->getSizeExpr());
+ const NonTypeTemplateParmDecl *NTTP =
+ getDeducedParameterFromExpr(Info, VectorParam->getSizeExpr());
if (!NTTP)
return Sema::TDK_Success;
return Sema::TDK_NonDeducedMismatch;
}
- NonTypeTemplateParmDecl *NTTP =
+ const NonTypeTemplateParmDecl *NTTP =
getDeducedParameterFromExpr(Info, ParamExpr);
if (!NTTP)
return Sema::TDK_Success;
return Result;
// Perform deduction on the address space, if we can.
- NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(
+ const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(
Info, AddressSpaceParam->getAddrSpaceExpr());
if (!NTTP)
return Sema::TDK_Success;
return Result;
// Perform deduction on the address space, if we can.
- NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(
+ const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(
Info, AddressSpaceParam->getAddrSpaceExpr());
if (!NTTP)
return Sema::TDK_Success;
if (IntParam->isUnsigned() != IntArg->isUnsigned())
return Sema::TDK_NonDeducedMismatch;
- NonTypeTemplateParmDecl *NTTP =
+ const NonTypeTemplateParmDecl *NTTP =
getDeducedParameterFromExpr(Info, IntParam->getNumBitsExpr());
if (!NTTP)
return Sema::TDK_Success;
return Sema::TDK_NonDeducedMismatch;
case TemplateArgument::Expression:
- if (NonTypeTemplateParmDecl *NTTP
- = getDeducedParameterFromExpr(Info, Param.getAsExpr())) {
+ if (const NonTypeTemplateParmDecl *NTTP =
+ getDeducedParameterFromExpr(Info, Param.getAsExpr())) {
if (Arg.getKind() == TemplateArgument::Integral)
return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP,
Arg.getAsIntegral(),
// from the length of the initializer list.
if (auto *DependentArrTy = dyn_cast_or_null<DependentSizedArrayType>(ArrTy)) {
// Determine the array bound is something we can deduce.
- if (NonTypeTemplateParmDecl *NTTP =
+ if (const NonTypeTemplateParmDecl *NTTP =
getDeducedParameterFromExpr(Info, DependentArrTy->getSizeExpr())) {
// We can perform template argument deduction for the given non-type
// template parameter.
if (const PackExpansionExpr *Expansion = dyn_cast<PackExpansionExpr>(E))
E = Expansion->getPattern();
- // Skip through any implicit casts we added while type-checking, and any
- // substitutions performed by template alias expansion.
- while (true) {
- if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E))
- E = ICE->getSubExpr();
- else if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(E))
- E = CE->getSubExpr();
- else if (const SubstNonTypeTemplateParmExpr *Subst =
- dyn_cast<SubstNonTypeTemplateParmExpr>(E))
- E = Subst->getReplacement();
- else
- break;
- }
-
- const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E);
- if (!DRE)
- return;
-
- const NonTypeTemplateParmDecl *NTTP
- = dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl());
+ const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(E, Depth);
if (!NTTP)
return;
llvm_unreachable("GUID declaration cannot be instantiated");
}
+Decl *TemplateDeclInstantiator::VisitTemplateParamObjectDecl(
+ TemplateParamObjectDecl *D) {
+ llvm_unreachable("template parameter objects cannot be instantiated");
+}
+
Decl *
TemplateDeclInstantiator::VisitLabelDecl(LabelDecl *D) {
LabelDecl *Inst = LabelDecl::Create(SemaRef.Context, Owner, D->getLocation(),
case Decl::Field:
case Decl::MSProperty:
case Decl::MSGuid:
+ case Decl::TemplateParamObject:
case Decl::ObjCIvar:
case Decl::ObjCAtDefsField:
case Decl::NonTypeTemplateParm:
void VisitFieldDecl(FieldDecl *FD);
void VisitMSPropertyDecl(MSPropertyDecl *FD);
void VisitMSGuidDecl(MSGuidDecl *D);
+ void VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D);
void VisitIndirectFieldDecl(IndirectFieldDecl *FD);
RedeclarableResult VisitVarDeclImpl(VarDecl *D);
void VisitVarDecl(VarDecl *VD) { VisitVarDeclImpl(VD); }
Reader.getContext().setPrimaryMergedDecl(D, Existing->getCanonicalDecl());
}
+void ASTDeclReader::VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D) {
+ VisitValueDecl(D);
+ D->Value = Record.readAPValue();
+
+ // Add this template parameter object to the AST context's lookup structure,
+ // and merge if needed.
+ if (TemplateParamObjectDecl *Existing =
+ Reader.getContext().TemplateParamObjectDecls.GetOrInsertNode(D))
+ Reader.getContext().setPrimaryMergedDecl(D, Existing->getCanonicalDecl());
+}
+
void ASTDeclReader::VisitIndirectFieldDecl(IndirectFieldDecl *FD) {
VisitValueDecl(FD);
VisitDecl(D);
D->ExtendingDecl = readDeclAs<ValueDecl>();
D->ExprWithTemporary = Record.readStmt();
- if (Record.readInt())
+ if (Record.readInt()) {
D->Value = new (D->getASTContext()) APValue(Record.readAPValue());
+ D->getASTContext().addDestruction(D->Value);
+ }
D->ManglingNumber = Record.readInt();
mergeMergeable(D);
}
case DECL_MS_GUID:
D = MSGuidDecl::CreateDeserialized(Context, ID);
break;
+ case DECL_TEMPLATE_PARAM_OBJECT:
+ D = TemplateParamObjectDecl::CreateDeserialized(Context, ID);
+ break;
case DECL_CAPTURED:
D = CapturedDecl::CreateDeserialized(Context, ID, Record.readInt());
break;
void VisitFieldDecl(FieldDecl *D);
void VisitMSPropertyDecl(MSPropertyDecl *D);
void VisitMSGuidDecl(MSGuidDecl *D);
+ void VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D);
void VisitIndirectFieldDecl(IndirectFieldDecl *D);
void VisitVarDecl(VarDecl *D);
void VisitImplicitParamDecl(ImplicitParamDecl *D);
Code = serialization::DECL_MS_GUID;
}
+void ASTDeclWriter::VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D) {
+ VisitValueDecl(D);
+ Record.AddAPValue(D->getValue());
+ Code = serialization::DECL_TEMPLATE_PARAM_OBJECT;
+}
+
void ASTDeclWriter::VisitIndirectFieldDecl(IndirectFieldDecl *D) {
VisitValueDecl(D);
Record.push_back(D->getChainingSize());
namespace dr368 { // dr368: yes
template<typename T, T> struct S {}; // expected-note {{here}}
template<typename T> int f(S<T, T()> *); // expected-error {{function type}}
- //template<typename T> int g(S<T, (T())> *); // FIXME: crashes clang
- template<typename T> int g(S<T, true ? T() : T()> *); // expected-note {{cannot have type 'dr368::X'}}
+ template<typename T> int g(S<T, (T())> *); // expected-note {{type 'dr368::X'}}
+ template<typename T> int g(S<T, true ? T() : T()> *); // expected-note {{type 'dr368::X'}}
struct X {};
int n = g<X>(0); // expected-error {{no matching}}
}
--- /dev/null
+// RUN: %clang_cc1 -std=c++20 -verify %s
+
+struct A { int n; };
+
+template<A a> struct B {
+ static constexpr A &v = a; // expected-error {{binding reference of type 'A' to value of type 'const A' drops 'const' qualifier}}
+};
+
+template<A a> struct C {
+ static constexpr const A &v = a;
+};
+
+// All such template parameters in the program of the same type with the same
+// value denote the same template parameter object.
+template<A a, typename T> void check() {
+ static_assert(&a == &T::v); // expected-error {{failed}}
+}
+
+using T = C<A{1}>;
+template void check<A{1}, T>();
+template void check<A{2}, T>(); // expected-note {{instantiation of}}
+
+// Different types with the same value are unequal.
+struct A2 { int n; };
+template<A2 a2> struct C2 {
+ static constexpr const A2 &v = a2;
+};
+static_assert((void*)&C<A{}>::v != (void*)&C2<A2{}>::v);
--- /dev/null
+// RUN: %clang_cc1 -std=c++20 -include %s %s -o %t
+
+// RUN: %clang_cc1 -std=c++20 -emit-pch %s -o %t
+// RUN: %clang_cc1 -std=c++20 -include-pch %t -verify %s
+
+// expected-no-diagnostics
+
+#ifndef HEADER
+#define HEADER
+
+struct A { int n; };
+
+template<A a> constexpr const A &get = a;
+
+constexpr const A &v = get<A{}>;
+
+#else /*included pch*/
+
+template<A a> constexpr const A &get2 = a;
+
+constexpr const A &v2 = get2<A{}>;
+
+static_assert(&v == &v2);
+
+#endif // HEADER
// expected-warning@-10 {{defaulted comparison operators are incompatible with C++ standards before C++20}}
#endif
};
+
+namespace NTTP {
+ struct A {};
+ template<A> struct Class {};
+#if __cplusplus <= 201703L
+ // expected-error@-2 {{non-type template parameter cannot have type 'NTTP::A' before C++20}}
+#else
+ // expected-warning@-4 {{non-type template parameter of type 'NTTP::A' is incompatible with C++ standards before C++20}}
+#endif
+}
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
+using size_t = __SIZE_TYPE__;
+
// 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}}
+union U { int a, b; } u;
int n; // expected-note 1+{{here}}
// pointers to subobjects
// 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}}
+using S123 = Struct<S{1, 2, 3}>;
+using S123 = Struct<S{1, 2, 3}>; // expected-note {{previous}}
+using S123 = Struct<S{1, 2, 4}>; // expected-error {{different types}}
+template<U> struct Union {};
+using U1 = Union<U{1}>;
+using U1 = Union<U{.a = 1}>; // expected-note {{previous}}
+using U1 = Union<U{.b = 1}>; // expected-error {{different types}}
// miscellaneous scalar types
template<_Complex int> struct ComplexInt {};
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}}
+
+namespace ClassNTTP {
+ struct A { // expected-note 2{{candidate}}
+ int x, y;
+ };
+ template<A a> constexpr int f() { return a.y; }
+ static_assert(f<A{1,2}>() == 2);
+
+ template<A a> int id;
+ constexpr A a = {1, 2};
+ static_assert(&id<A{1,2}> == &id<a>);
+ static_assert(&id<A{1,3}> != &id<a>);
+
+ int k = id<1>; // expected-error {{no viable conversion from 'int' to 'ClassNTTP::A'}}
+
+ struct B {
+ constexpr B() {}
+ constexpr B(int) = delete; // expected-note {{here}}
+ };
+ template<B> struct Q {}; // expected-note {{passing argument to parameter here}}
+ Q<1> q; // expected-error {{conversion function from 'int' to 'ClassNTTP::B' invokes a deleted function}}
+
+ struct C {
+ constexpr C() {}
+ C(const C&) = delete; // expected-note {{here}}
+ };
+ template<C> struct R {}; // expected-note {{passing argument to parameter here}}
+ constexpr C c;
+ R<c> r; // expected-error {{call to deleted constructor}}
+}
+
+namespace ConvertedConstant {
+ struct A {
+ constexpr A(float) {}
+ };
+ template <A> struct X {};
+ void f(X<1.0f>) {} // OK, user-defined conversion
+ void f(X<2>) {} // expected-error {{conversion from 'int' to 'ConvertedConstant::A' is not allowed in a converted constant expression}}
+}
+
+namespace CopyCounting {
+ // Make sure we don't use the copy constructor when transferring the "same"
+ // template parameter object around.
+ struct A { int n; constexpr A(int n = 0) : n(n) {} constexpr A(const A &a) : n(a.n+1) {} };
+ template<A a> struct X {};
+ template<A a> constexpr int f(X<a> x) { return a.n; }
+
+ static_assert(f(X<A{}>()) == 0);
+
+ template<A a> struct Y { void f(); };
+ template<A a> void g(Y<a> y) { y.Y<a>::f(); }
+ void h() { constexpr A a; g<a>(Y<a>{}); }
+
+ template<A a> struct Z {
+ constexpr int f() {
+ constexpr A v = a; // this is {a.n+1}
+ return Z<v>().f() + 1; // this is Z<{a.n+2}>
+ }
+ };
+ template<> struct Z<A{20}> {
+ constexpr int f() {
+ return 32;
+ }
+ };
+ static_assert(Z<A{}>().f() == 42);
+}
+
+namespace StableAddress {
+ template<size_t N> struct str {
+ char arr[N];
+ };
+ // FIXME: Deduction guide not needed with P1816R0.
+ template<size_t N> str(const char (&)[N]) -> str<N>;
+
+ // FIXME: Explicit size not needed.
+ template<str<15> s> constexpr int sum() {
+ int n = 0;
+ for (char c : s.arr)
+ n += c;
+ return n;
+ }
+ static_assert(sum<str{"$hello $world."}>() == 1234);
+}
+
+namespace TemplateSpecializations {
+ struct A { int arr[10]; };
+ template<A> struct X; // expected-note {{here}}
+
+ using T = X<A{1, 2, 3}>;
+ using T = X<A{1, 2, 3, 0}>;
+ using T = X<A{1, 2, 3, 0, 0}>;
+ using T = X<A{1, 2, 3, 0, 0, 0}>;
+
+ template<> struct X<A{1, 2, 3, 4}> {};
+ X<A{1, 2, 3, 4, 0}> x;
+
+ template<auto V, auto W> constexpr bool Same = false;
+ template<auto V> constexpr bool Same<V, V> = true;
+ static_assert(Same<A{}, A{0, 0}>);
+ static_assert(Same<A{1}, A{1, 0}>);
+ static_assert(!Same<A{1}, A{1, 1}>);
+
+ // We can't directly specialize on member values...
+ template<int N> // expected-note {{parameter 'N'}}
+ struct X<A{N, N}> {}; // expected-error {{cannot be deduced}}
+
+ // ... but we can fake it up.
+ // template<int N> struct X<A{N, N}>
+ template <A V> requires Same<V, A{V.arr[0], V.arr[0]}>
+ struct X<V> {
+ static constexpr bool match = true;
+ };
+ static_assert(X<A{1, 1}>::match);
+ static_assert(X<A{2, 2}>::match);
+ static_assert(X<A{1, 2}>::match); // expected-error {{undefined}}
+
+ template<int, A> struct Y; // expected-note {{here}}
+ template<int N> struct Y<N, A{N, N, N}> {};
+ Y<1, A{1, 1, 1, 0}> y1;
+ Y<1, A{1, 1, 1, 1}> y2; // expected-error {{undefined}}
+
+ template<A, A> struct Z; // expected-note {{here}}
+ template<A V> struct Z<V, V> {};
+ Z<A{1, 2}, A{1, 2, 0}> z1;
+ Z<A{1, 2}, A{1, 3}> z2; // expected-error {{undefined}}
+
+ template struct Z<A{1}, A{1, 0}>;
+}
+
+namespace Diags {
+ struct A { int n, m; };
+ template<A a> struct X { static_assert(a.n == a.m); }; // expected-error {{static_assert failed due to requirement 'Diags::A{1, 2}.n == Diags::A{1, 2}.m'}}
+ template struct X<A{1, 2}>; // expected-note {{in instantiation of template class 'Diags::X<{1, 2}>' requested here}}
+}
case Decl::Binding:
case Decl::MSProperty:
case Decl::MSGuid:
+ case Decl::TemplateParamObject:
case Decl::IndirectField:
case Decl::ObjCIvar:
case Decl::ObjCAtDefsField: