[clang] ba4768c - [c++20] For P0732R2 / P1907R1: Basic frontend support for class types as
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 21 13:21:59 PDT 2020
Author: Richard Smith
Date: 2020-10-21T13:21:41-07:00
New Revision: ba4768c966581658465f7366df9b0811f468a2d7
URL: https://github.com/llvm/llvm-project/commit/ba4768c966581658465f7366df9b0811f468a2d7
DIFF: https://github.com/llvm/llvm-project/commit/ba4768c966581658465f7366df9b0811f468a2d7.diff
LOG: [c++20] For P0732R2 / P1907R1: Basic frontend support for class types as
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.
Added:
clang/test/CXX/temp/temp.param/p8-cxx20.cpp
clang/test/PCH/cxx20-template-args.cpp
Modified:
clang/include/clang/AST/ASTContext.h
clang/include/clang/AST/DeclTemplate.h
clang/include/clang/AST/RecursiveASTVisitor.h
clang/include/clang/Basic/DeclNodes.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Initialization.h
clang/include/clang/Sema/Sema.h
clang/include/clang/Serialization/ASTBitCodes.h
clang/lib/AST/ASTContext.cpp
clang/lib/AST/ASTDiagnostic.cpp
clang/lib/AST/DeclBase.cpp
clang/lib/AST/DeclTemplate.cpp
clang/lib/AST/ExprClassification.cpp
clang/lib/AST/ExprConstant.cpp
clang/lib/AST/ItaniumMangle.cpp
clang/lib/AST/MicrosoftMangle.cpp
clang/lib/AST/StmtPrinter.cpp
clang/lib/AST/TemplateBase.cpp
clang/lib/CodeGen/CGDecl.cpp
clang/lib/CodeGen/CGExpr.cpp
clang/lib/CodeGen/CGExprConstant.cpp
clang/lib/CodeGen/CodeGenModule.cpp
clang/lib/CodeGen/CodeGenModule.h
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaInit.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateDeduction.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Serialization/ASTCommon.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
clang/test/CXX/drs/dr3xx.cpp
clang/test/SemaCXX/cxx17-compat.cpp
clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
clang/tools/libclang/CIndex.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index d8c0b624ef71..30d910c93022 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -289,6 +289,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// 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.
///
@@ -2868,6 +2871,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// 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;
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 5f3525739091..7a175db8cb16 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3226,7 +3226,7 @@ class VarTemplateDecl : public RedeclarableTemplateDecl {
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;
@@ -3255,6 +3255,9 @@ class ConceptDecl : public TemplateDecl, public Mergeable<ConceptDecl> {
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; }
@@ -3264,6 +3267,74 @@ class ConceptDecl : public TemplateDecl, public Mergeable<ConceptDecl> {
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;
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 6f07b92f2532..5e83cded0652 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1970,6 +1970,8 @@ DEF_TRAVERSE_DECL(MSPropertyDecl, { TRY_TO(TraverseDeclaratorHelper(D)); })
DEF_TRAVERSE_DECL(MSGuidDecl, {})
+DEF_TRAVERSE_DECL(TemplateParamObjectDecl, {})
+
DEF_TRAVERSE_DECL(FieldDecl, {
TRY_TO(TraverseDeclaratorHelper(D));
if (D->isBitField())
diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index 866988ee3f01..4771a3549426 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -41,6 +41,7 @@ def Named : DeclNode<Decl, "named declarations", 1>;
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>;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index ee942afd440a..641d3e73905e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1994,8 +1994,8 @@ def err_destructor_template : Error<
// 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 "
@@ -2137,7 +2137,7 @@ def warn_unsequenced_mod_use : Warning<
"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|"
@@ -4492,6 +4492,10 @@ def note_not_structural_rvalue_ref_field : Note<
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">,
diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h
index ca9e0a198cb9..6976e7c95c8b 100644
--- a/clang/include/clang/Sema/Initialization.h
+++ b/clang/include/clang/Sema/Initialization.h
@@ -55,6 +55,9 @@ class alignas(8) InitializedEntity {
/// 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,
@@ -175,7 +178,8 @@ class alignas(8) InitializedEntity {
};
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
@@ -281,6 +285,17 @@ class alignas(8) InitializedEntity {
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) {
@@ -441,6 +456,10 @@ class alignas(8) InitializedEntity {
getKind() == EK_Parameter_CF_Audited);
}
+ bool isParamOrTemplateParamKind() const {
+ return isParameterKind() || getKind() == EK_TemplateParameter;
+ }
+
/// Determine whether this initialization consumes the
/// parameter.
bool isParameterConsumed() const {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 7ced3d9a7bd4..18f115003f38 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3369,7 +3369,8 @@ class Sema final {
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.
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index c6f9f1d1a08f..a2ae032ed5b8 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1281,6 +1281,9 @@ class TypeIdx {
/// A MSGuidDecl record.
DECL_MS_GUID,
+ /// A TemplateParamObjectDecl record.
+ DECL_TEMPLATE_PARAM_OBJECT,
+
/// A VarDecl record.
DECL_VAR,
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 32bb3f991d95..4f3566a66962 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -4870,9 +4870,16 @@ TemplateArgument ASTContext::getInjectedTemplateArg(NamedDecl *Param) {
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())
@@ -10962,6 +10969,27 @@ ASTContext::getMSGuidDecl(MSGuidDecl::Parts Parts) const {
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())
diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp
index 99ce46e83123..2bc731717b98 100644
--- a/clang/lib/AST/ASTDiagnostic.cpp
+++ b/clang/lib/AST/ASTDiagnostic.cpp
@@ -1834,7 +1834,14 @@ class TemplateDiff {
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;
}
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index f2502c327a11..0656efae5489 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -835,6 +835,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case ExternCContext:
case Decomposition:
case MSGuid:
+ case TemplateParamObject:
case UsingDirective:
case BuiltinTemplate:
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index d99a9c19c506..9918377070c3 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1431,3 +1431,36 @@ void TypeConstraint::print(llvm::raw_ostream &OS, PrintingPolicy Policy) const {
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());
+}
diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp
index 31aa734ffedb..0286c730ce4e 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -453,12 +453,14 @@ static Cl::Kinds ClassifyDecl(ASTContext &Ctx, const Decl *D) {
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)));
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index b2ddca7f7ce0..7a933e3eaafb 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1978,6 +1978,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
// ... 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);
@@ -3980,6 +3982,16 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
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,
@@ -8026,14 +8038,13 @@ static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info,
}
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);
}
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 26fc7f8ec816..2417da392876 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -660,7 +660,12 @@ void CXXNameMangler::mangle(GlobalDecl GD) {
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");
}
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index f6e6fefe98d2..227f5279d63d 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -503,7 +503,12 @@ void MicrosoftCXXNameMangler::mangle(const NamedDecl *D, StringRef Prefix) {
// 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!");
}
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 1390876dc0df..55a721194ccf 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -970,6 +970,10 @@ void StmtPrinter::VisitDeclRefExpr(DeclRefExpr *Node) {
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())
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index 891c9bd0fd7a..22af2545779d 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -352,6 +352,13 @@ void TemplateArgument::print(const PrintingPolicy &Policy,
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);
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 7daf91ff73e3..96922e415423 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -117,6 +117,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
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:
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index ed2c8e6a71f1..28be81c919b2 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2827,6 +2827,10 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
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");
}
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 011d39cfa294..ca1d3a937fa8 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1905,6 +1905,9 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
if (auto *GD = dyn_cast<MSGuidDecl>(D))
return CGM.GetAddrOfMSGuidDecl(GD);
+ if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(D))
+ return CGM.GetAddrOfTemplateParamObject(TPO);
+
return nullptr;
}
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 93b49ec981e8..8b53ec9ab4a9 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -2542,6 +2542,12 @@ ConstantAddress CodeGenModule::GetAddrOfMSGuidDecl(const MSGuidDecl *GD) {
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?");
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 088ed2830fb8..fc732dbea521 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -859,6 +859,10 @@ class CodeGenModule : public CodeGenTypeCache {
/// 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);
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 7b4e9edbfbf3..e844ad34240d 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -3226,6 +3226,17 @@ ExprResult Sema::BuildDeclarationNameExpr(
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;
@@ -3325,8 +3336,9 @@ ExprResult Sema::BuildDeclarationNameExpr(
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;
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 485cb85f517f..419b25d278f5 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -1123,6 +1123,7 @@ static void warnBracedScalarInit(Sema &S, const InitializedEntity &Entity,
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;
@@ -3283,6 +3284,7 @@ DeclarationName InitializedEntity::getName() const {
case EK_Variable:
case EK_Member:
case EK_Binding:
+ case EK_TemplateParameter:
return Variable.VariableOrMember->getDeclName();
case EK_LambdaCapture:
@@ -3313,6 +3315,7 @@ ValueDecl *InitializedEntity::getDecl() const {
case EK_Variable:
case EK_Member:
case EK_Binding:
+ case EK_TemplateParameter:
return Variable.VariableOrMember;
case EK_Parameter:
@@ -3350,6 +3353,7 @@ bool InitializedEntity::allowsNRVO() const {
case EK_Variable:
case EK_Parameter:
case EK_Parameter_CF_Audited:
+ case EK_TemplateParameter:
case EK_Member:
case EK_Binding:
case EK_New:
@@ -3381,6 +3385,7 @@ unsigned InitializedEntity::dumpImpl(raw_ostream &OS) const {
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;
@@ -6001,6 +6006,11 @@ getAssignmentAction(const InitializedEntity &Entity, bool Diagnose = false) {
// 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:
@@ -6035,6 +6045,7 @@ static bool shouldBindAsTemporary(const InitializedEntity &Entity) {
case InitializedEntity::EK_LambdaToBlockConversionBlockElement:
case InitializedEntity::EK_LambdaCapture:
case InitializedEntity::EK_CompoundLiteralInit:
+ case InitializedEntity::EK_TemplateParameter:
return false;
case InitializedEntity::EK_Parameter:
@@ -6069,6 +6080,7 @@ static bool shouldDestroyEntity(const InitializedEntity &Entity) {
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:
@@ -6102,6 +6114,7 @@ static SourceLocation getInitializationLoc(const InitializedEntity &Entity,
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:
@@ -6345,7 +6358,7 @@ static void CheckCXX98CompatAccessibleCopy(Sema &S,
void InitializationSequence::PrintInitLocationNote(Sema &S,
const InitializedEntity &Entity) {
- if (Entity.isParameterKind() && Entity.getDecl()) {
+ if (Entity.isParamOrTemplateParamKind() && Entity.getDecl()) {
if (Entity.getDecl()->getLocation().isInvalid())
return;
@@ -6611,6 +6624,10 @@ static LifetimeResult getEntityLifetime(
// 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
@@ -7882,7 +7899,7 @@ ExprResult InitializationSequence::Perform(Sema &S,
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.
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index d2eabd0c0512..7da2a8c2d84f 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5571,7 +5571,8 @@ static bool CheckConvertedConstantConversions(Sema &S,
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");
@@ -5601,9 +5602,10 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
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:
@@ -5630,8 +5632,20 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
<< 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;
@@ -5724,8 +5738,10 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
}
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,
@@ -5734,7 +5750,8 @@ 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;
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 660964c8f92b..4017e65118d9 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1441,6 +1441,7 @@ QualType Sema::CheckNonTypeTemplateParameterType(QualType T,
return QualType();
}
+ Diag(Loc, diag::warn_cxx17_compat_template_nontype_parm_type) << T;
return T.getUnqualifiedType();
}
@@ -6857,9 +6858,12 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
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.
@@ -6915,12 +6919,38 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
*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();
@@ -6931,8 +6961,6 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
return ArgResult;
}
- QualType CanonParamType = Context.getCanonicalType(ParamType);
-
// Convert the APValue to a TemplateArgument.
switch (Value.getKind()) {
case APValue::None:
@@ -7002,6 +7030,13 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
: 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:
@@ -7010,8 +7045,6 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
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;
}
@@ -7505,6 +7538,11 @@ Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg,
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");
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index b266f360a138..1c20ca619138 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -171,30 +171,41 @@ static void MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
/// 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) {
@@ -374,7 +385,7 @@ checkDeducedTemplateArguments(ASTContext &Context,
/// 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() &&
@@ -383,7 +394,7 @@ static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument(
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;
@@ -410,10 +421,16 @@ static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument(
// 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());
}
@@ -421,7 +438,7 @@ static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument(
/// 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(
@@ -435,7 +452,7 @@ static Sema::TemplateDeductionResult 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 =
@@ -454,7 +471,7 @@ static Sema::TemplateDeductionResult DeduceNullPtrTemplateArgument(
/// \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),
@@ -467,7 +484,7 @@ static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument(
/// \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;
@@ -1757,7 +1774,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
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;
@@ -1826,7 +1843,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
// 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() &&
@@ -2015,7 +2032,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
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;
@@ -2039,7 +2056,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
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;
@@ -2068,8 +2085,8 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
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;
@@ -2094,8 +2111,8 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
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;
@@ -2171,7 +2188,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
return Sema::TDK_NonDeducedMismatch;
}
- NonTypeTemplateParmDecl *NTTP =
+ const NonTypeTemplateParmDecl *NTTP =
getDeducedParameterFromExpr(Info, ParamExpr);
if (!NTTP)
return Sema::TDK_Success;
@@ -2218,7 +2235,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
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;
@@ -2241,7 +2258,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
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;
@@ -2260,7 +2277,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
if (IntParam->isUnsigned() != IntArg->isUnsigned())
return Sema::TDK_NonDeducedMismatch;
- NonTypeTemplateParmDecl *NTTP =
+ const NonTypeTemplateParmDecl *NTTP =
getDeducedParameterFromExpr(Info, IntParam->getNumBitsExpr());
if (!NTTP)
return Sema::TDK_Success;
@@ -2377,8 +2394,8 @@ DeduceTemplateArguments(Sema &S,
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(),
@@ -3982,7 +3999,7 @@ static Sema::TemplateDeductionResult DeduceFromInitializerList(
// 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.
@@ -5728,26 +5745,7 @@ MarkUsedTemplateParameters(ASTContext &Ctx,
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;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 525f3031573a..51f74d0b1d99 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -737,6 +737,11 @@ Decl *TemplateDeclInstantiator::VisitMSGuidDecl(MSGuidDecl *D) {
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(),
diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp
index bf583b02f96b..54c7f051459f 100644
--- a/clang/lib/Serialization/ASTCommon.cpp
+++ b/clang/lib/Serialization/ASTCommon.cpp
@@ -378,6 +378,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
case Decl::Field:
case Decl::MSProperty:
case Decl::MSGuid:
+ case Decl::TemplateParamObject:
case Decl::ObjCIvar:
case Decl::ObjCAtDefsField:
case Decl::NonTypeTemplateParm:
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 4d50e4af31f7..fd3b981150a0 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -369,6 +369,7 @@ namespace clang {
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); }
@@ -1377,6 +1378,17 @@ void ASTDeclReader::VisitMSGuidDecl(MSGuidDecl *D) {
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);
@@ -2409,8 +2421,10 @@ void ASTDeclReader::VisitLifetimeExtendedTemporaryDecl(
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);
}
@@ -3983,6 +3997,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) {
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;
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 5b0a6ffb9566..df7304457419 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -96,6 +96,7 @@ namespace clang {
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);
@@ -965,6 +966,12 @@ void ASTDeclWriter::VisitMSGuidDecl(MSGuidDecl *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());
diff --git a/clang/test/CXX/drs/dr3xx.cpp b/clang/test/CXX/drs/dr3xx.cpp
index be3169ceab76..cec9acf63a9e 100644
--- a/clang/test/CXX/drs/dr3xx.cpp
+++ b/clang/test/CXX/drs/dr3xx.cpp
@@ -906,8 +906,8 @@ namespace dr367 { // dr367: yes
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}}
}
diff --git a/clang/test/CXX/temp/temp.param/p8-cxx20.cpp b/clang/test/CXX/temp/temp.param/p8-cxx20.cpp
new file mode 100644
index 000000000000..7a31846756c3
--- /dev/null
+++ b/clang/test/CXX/temp/temp.param/p8-cxx20.cpp
@@ -0,0 +1,28 @@
+// 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);
diff --git a/clang/test/PCH/cxx20-template-args.cpp b/clang/test/PCH/cxx20-template-args.cpp
new file mode 100644
index 000000000000..f95ec52dca27
--- /dev/null
+++ b/clang/test/PCH/cxx20-template-args.cpp
@@ -0,0 +1,25 @@
+// 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
diff --git a/clang/test/SemaCXX/cxx17-compat.cpp b/clang/test/SemaCXX/cxx17-compat.cpp
index b65ed3ea340b..41a465ac613e 100644
--- a/clang/test/SemaCXX/cxx17-compat.cpp
+++ b/clang/test/SemaCXX/cxx17-compat.cpp
@@ -121,3 +121,13 @@ struct DefaultedComparisons {
// expected-warning at -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 at -2 {{non-type template parameter cannot have type 'NTTP::A' before C++20}}
+#else
+ // expected-warning at -4 {{non-type template parameter of type 'NTTP::A' is incompatible with C++ standards before C++20}}
+#endif
+}
diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
index aa9e71ff1f6e..d0ace9b2b2b5 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
@@ -1,11 +1,14 @@
// 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
@@ -31,8 +34,13 @@ using IP3 = IntRef<*(s.n + 3)>; // expected-error {{not a constant expression}}
// 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 {{
diff erent 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 {{
diff erent types}}
// miscellaneous scalar types
template<_Complex int> struct ComplexInt {};
@@ -42,3 +50,137 @@ 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}}
+
+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}}
+}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index a64f90f8f74b..3baff9d04b86 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -6376,6 +6376,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
case Decl::Binding:
case Decl::MSProperty:
case Decl::MSGuid:
+ case Decl::TemplateParamObject:
case Decl::IndirectField:
case Decl::ObjCIvar:
case Decl::ObjCAtDefsField:
More information about the cfe-commits
mailing list