forked from OSchip/llvm-project
[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:
parent
1d1217c4ea
commit
ba4768c966
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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">,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1281,6 +1281,9 @@ public:
|
|||
/// A MSGuidDecl record.
|
||||
DECL_MS_GUID,
|
||||
|
||||
/// A TemplateParamObjectDecl record.
|
||||
DECL_TEMPLATE_PARAM_OBJECT,
|
||||
|
||||
/// A VarDecl record.
|
||||
DECL_VAR,
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -835,6 +835,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
|
|||
case ExternCContext:
|
||||
case Decomposition:
|
||||
case MSGuid:
|
||||
case TemplateParamObject:
|
||||
|
||||
case UsingDirective:
|
||||
case BuiltinTemplate:
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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!");
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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?");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
|
@ -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);
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue