[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.
This commit is contained in:
Richard Smith 2020-09-20 23:16:08 -07:00
parent 1d1217c4ea
commit ba4768c966
38 changed files with 645 additions and 97 deletions

View File

@ -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 @@ public:
/// 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;

View File

@ -3226,7 +3226,7 @@ public:
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 @@ public:
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 @@ public:
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;

View File

@ -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())

View File

@ -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>;

View File

@ -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">,

View File

@ -55,6 +55,9 @@ public:
/// 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 @@ private:
};
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 @@ public:
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 @@ public:
getKind() == EK_Parameter_CF_Audited);
}
bool isParamOrTemplateParamKind() const {
return isParameterKind() || getKind() == EK_TemplateParameter;
}
/// Determine whether this initialization consumes the
/// parameter.
bool isParameterConsumed() const {

View File

@ -3369,7 +3369,8 @@ public:
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.

View File

@ -1281,6 +1281,9 @@ public:
/// A MSGuidDecl record.
DECL_MS_GUID,
/// A TemplateParamObjectDecl record.
DECL_TEMPLATE_PARAM_OBJECT,
/// A VarDecl record.
DECL_VAR,

View File

@ -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())

View File

@ -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;
}

View File

@ -835,6 +835,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case ExternCContext:
case Decomposition:
case MSGuid:
case TemplateParamObject:
case UsingDirective:
case BuiltinTemplate:

View File

@ -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());
}

View File

@ -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)));

View File

@ -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);
}

View File

@ -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");
}

View File

@ -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!");
}

View File

@ -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())

View File

@ -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);

View File

@ -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:

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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?");

View File

@ -859,6 +859,10 @@ public:
/// 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);

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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");

View File

@ -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;

View File

@ -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(),

View File

@ -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:

View File

@ -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;

View File

@ -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());

View File

@ -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}}
}

View File

@ -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);

View File

@ -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

View File

@ -121,3 +121,13 @@ struct DefaultedComparisons {
// 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
}

View File

@ -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 {{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 {};
@ -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}}
}

View File

@ -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: