[clang] caf30e7 - [c++20] For P0732R2: Give class NTTPs the proper type when examined with 'decltype'.
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 21 14:16:08 PDT 2020
Author: Richard Smith
Date: 2020-10-21T14:15:54-07:00
New Revision: caf30e7f03566f20c45e5d0895e71c4bff9056ef
URL: https://github.com/llvm/llvm-project/commit/caf30e7f03566f20c45e5d0895e71c4bff9056ef
DIFF: https://github.com/llvm/llvm-project/commit/caf30e7f03566f20c45e5d0895e71c4bff9056ef.diff
LOG: [c++20] For P0732R2: Give class NTTPs the proper type when examined with 'decltype'.
This requires that we track enough information to determine the original
type of the parameter in a substituted non-type template parameter, to
distinguish the reference-to-class case from the class case.
Added:
clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.decltype/p1.cpp
Modified:
clang/include/clang/AST/ExprCXX.h
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/ExprCXX.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/SemaType.cpp
clang/lib/Serialization/ASTReaderStmt.cpp
clang/lib/Serialization/ASTWriterStmt.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 926021e9c6ed..61a23ddaa368 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -4233,8 +4233,10 @@ class SubstNonTypeTemplateParmExpr : public Expr {
friend class ASTReader;
friend class ASTStmtReader;
- /// The replaced parameter.
- NonTypeTemplateParmDecl *Param;
+ /// The replaced parameter and a flag indicating if it was a reference
+ /// parameter. For class NTTPs, we can't determine that based on the value
+ /// category alone.
+ llvm::PointerIntPair<NonTypeTemplateParmDecl*, 1, bool> ParamAndRef;
/// The replacement expression.
Stmt *Replacement;
@@ -4245,10 +4247,10 @@ class SubstNonTypeTemplateParmExpr : public Expr {
public:
SubstNonTypeTemplateParmExpr(QualType Ty, ExprValueKind ValueKind,
SourceLocation Loc,
- NonTypeTemplateParmDecl *Param,
+ NonTypeTemplateParmDecl *Param, bool RefParam,
Expr *Replacement)
: Expr(SubstNonTypeTemplateParmExprClass, Ty, ValueKind, OK_Ordinary),
- Param(Param), Replacement(Replacement) {
+ ParamAndRef(Param, RefParam), Replacement(Replacement) {
SubstNonTypeTemplateParmExprBits.NameLoc = Loc;
setDependence(computeDependence(this));
}
@@ -4261,7 +4263,14 @@ class SubstNonTypeTemplateParmExpr : public Expr {
Expr *getReplacement() const { return cast<Expr>(Replacement); }
- NonTypeTemplateParmDecl *getParameter() const { return Param; }
+ NonTypeTemplateParmDecl *getParameter() const {
+ return ParamAndRef.getPointer();
+ }
+
+ bool isReferenceParameter() const { return ParamAndRef.getInt(); }
+
+ /// Determine the substituted type of the template parameter.
+ QualType getParameterType(const ASTContext &Ctx) const;
static bool classof(const Stmt *s) {
return s->getStmtClass() == SubstNonTypeTemplateParmExprClass;
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 23720bf75a65..ce79b99d7043 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -7859,7 +7859,8 @@ ExpectedStmt ASTNodeImporter::VisitSubstNonTypeTemplateParmExpr(
return std::move(Err);
return new (Importer.getToContext()) SubstNonTypeTemplateParmExpr(
- ToType, E->getValueKind(), ToExprLoc, ToParameter, ToReplacement);
+ ToType, E->getValueKind(), ToExprLoc, ToParameter,
+ E->isReferenceParameter(), ToReplacement);
}
ExpectedStmt ASTNodeImporter::VisitTypeTraitExpr(TypeTraitExpr *E) {
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 4a421f03e589..c1ec86075772 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1565,6 +1565,15 @@ SizeOfPackExpr *SizeOfPackExpr::CreateDeserialized(ASTContext &Context,
return new (Storage) SizeOfPackExpr(EmptyShell(), NumPartialArgs);
}
+QualType SubstNonTypeTemplateParmExpr::getParameterType(
+ const ASTContext &Context) const {
+ // Note that, for a class type NTTP, we will have an lvalue of type 'const
+ // T', so we can't just compute this from the type and value category.
+ if (isReferenceParameter())
+ return Context.getLValueReferenceType(getType());
+ return getType().getUnqualifiedType();
+}
+
SubstNonTypeTemplateParmPackExpr::SubstNonTypeTemplateParmPackExpr(
QualType T, ExprValueKind ValueKind, NonTypeTemplateParmDecl *Param,
SourceLocation NameLoc, const TemplateArgument &ArgPack)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index a13118839036..d01189b42ed6 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1464,9 +1464,12 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
if (TargetType.isNull())
return ExprError();
+ QualType ExprType = TargetType.getNonLValueExprType(SemaRef.Context);
+ if (TargetType->isRecordType())
+ ExprType.addConst();
+
return new (SemaRef.Context) SubstNonTypeTemplateParmPackExpr(
- TargetType.getNonLValueExprType(SemaRef.Context),
- TargetType->isReferenceType() ? VK_LValue : VK_RValue, NTTP,
+ ExprType, TargetType->isReferenceType() ? VK_LValue : VK_RValue, NTTP,
E->getLocation(), Arg);
}
@@ -1498,15 +1501,39 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
SourceLocation loc,
TemplateArgument arg) {
ExprResult result;
- QualType type;
- // The template argument itself might be an expression, in which
- // case we just return that expression.
+ // Determine the substituted parameter type. We can usually infer this from
+ // the template argument, but not always.
+ auto SubstParamType = [&] {
+ QualType T;
+ if (parm->isExpandedParameterPack())
+ T = parm->getExpansionType(SemaRef.ArgumentPackSubstitutionIndex);
+ else
+ T = parm->getType();
+ if (parm->isParameterPack() && isa<PackExpansionType>(T))
+ T = cast<PackExpansionType>(T)->getPattern();
+ return SemaRef.SubstType(T, TemplateArgs, loc, parm->getDeclName());
+ };
+
+ bool refParam = false;
+
+ // The template argument itself might be an expression, in which case we just
+ // return that expression. This happens when substituting into an alias
+ // template.
if (arg.getKind() == TemplateArgument::Expression) {
Expr *argExpr = arg.getAsExpr();
result = argExpr;
- type = argExpr->getType();
-
+ if (argExpr->isLValue()) {
+ if (argExpr->getType()->isRecordType()) {
+ // Check whether the parameter was actually a reference.
+ QualType paramType = SubstParamType();
+ if (paramType.isNull())
+ return ExprError();
+ refParam = paramType->isReferenceType();
+ } else {
+ refParam = true;
+ }
+ }
} else if (arg.getKind() == TemplateArgument::Declaration ||
arg.getKind() == TemplateArgument::NullPtr) {
ValueDecl *VD;
@@ -1524,36 +1551,25 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
VD = nullptr;
}
- // Derive the type we want the substituted decl to have. This had
- // better be non-dependent, or these checks will have serious problems.
- if (parm->isExpandedParameterPack()) {
- type = parm->getExpansionType(SemaRef.ArgumentPackSubstitutionIndex);
- } else if (parm->isParameterPack() &&
- isa<PackExpansionType>(parm->getType())) {
- type = SemaRef.SubstType(
- cast<PackExpansionType>(parm->getType())->getPattern(),
- TemplateArgs, loc, parm->getDeclName());
- } else {
- type = SemaRef.SubstType(VD ? arg.getParamTypeForDecl() : arg.getNullPtrType(),
- TemplateArgs, loc, parm->getDeclName());
- }
- assert(!type.isNull() && "type substitution failed for param type");
- assert(!type->isDependentType() && "param type still dependent");
- result = SemaRef.BuildExpressionFromDeclTemplateArgument(arg, type, loc);
-
- if (!result.isInvalid()) type = result.get()->getType();
+ QualType paramType = VD ? arg.getParamTypeForDecl() : arg.getNullPtrType();
+ assert(!paramType.isNull() && "type substitution failed for param type");
+ assert(!paramType->isDependentType() && "param type still dependent");
+ result = SemaRef.BuildExpressionFromDeclTemplateArgument(arg, paramType, loc);
+ refParam = paramType->isReferenceType();
} else {
result = SemaRef.BuildExpressionFromIntegralTemplateArgument(arg, loc);
-
- // Note that this type can be
diff erent from the type of 'result',
- // e.g. if it's an enum type.
- type = arg.getIntegralType();
+ assert(result.isInvalid() ||
+ SemaRef.Context.hasSameType(result.get()->getType(),
+ arg.getIntegralType()));
}
- if (result.isInvalid()) return ExprError();
+
+ if (result.isInvalid())
+ return ExprError();
Expr *resultExpr = result.get();
return new (SemaRef.Context) SubstNonTypeTemplateParmExpr(
- type, resultExpr->getValueKind(), loc, parm, resultExpr);
+ resultExpr->getType(), resultExpr->getValueKind(), loc, parm, refParam,
+ resultExpr);
}
ExprResult
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 136f03a69b25..4d156634bf48 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -8890,7 +8890,17 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) {
// C++11 [dcl.type.simple]p4:
// The type denoted by decltype(e) is defined as follows:
- //
+
+ // C++20:
+ // - if E is an unparenthesized id-expression naming a non-type
+ // template-parameter (13.2), decltype(E) is the type of the
+ // template-parameter after performing any necessary type deduction
+ // Note that this does not pick up the implicit 'const' for a template
+ // parameter object. This rule makes no
diff erence before C++20 so we apply
+ // it unconditionally.
+ if (const auto *SNTTPE = dyn_cast<SubstNonTypeTemplateParmExpr>(E))
+ return SNTTPE->getParameterType(S.Context);
+
// - if e is an unparenthesized id-expression or an unparenthesized class
// member access (5.2.5), decltype(e) is the type of the entity named
// by e. If there is no such entity, or if e names a set of overloaded
@@ -8899,6 +8909,8 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) {
// We apply the same rules for Objective-C ivar and property references.
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
const ValueDecl *VD = DRE->getDecl();
+ if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(VD))
+ return TPO->getType().getUnqualifiedType();
return VD->getType();
} else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
if (const ValueDecl *VD = ME->getMemberDecl())
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 363527f884b3..cf335302f7f0 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2121,7 +2121,8 @@ void ASTStmtReader::VisitSizeOfPackExpr(SizeOfPackExpr *E) {
void ASTStmtReader::VisitSubstNonTypeTemplateParmExpr(
SubstNonTypeTemplateParmExpr *E) {
VisitExpr(E);
- E->Param = readDeclAs<NonTypeTemplateParmDecl>();
+ E->ParamAndRef.setPointer(readDeclAs<NonTypeTemplateParmDecl>());
+ E->ParamAndRef.setInt(Record.readInt());
E->SubstNonTypeTemplateParmExprBits.NameLoc = readSourceLocation();
E->Replacement = Record.readSubExpr();
}
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 0121f2583207..522cd3247f34 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2018,6 +2018,7 @@ void ASTStmtWriter::VisitSubstNonTypeTemplateParmExpr(
SubstNonTypeTemplateParmExpr *E) {
VisitExpr(E);
Record.AddDeclRef(E->getParameter());
+ Record.push_back(E->isReferenceParameter());
Record.AddSourceLocation(E->getNameLoc());
Record.AddStmt(E->getReplacement());
Code = serialization::EXPR_SUBST_NON_TYPE_TEMPLATE_PARM;
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.decltype/p1.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.decltype/p1.cpp
new file mode 100644
index 000000000000..a62b73d9915e
--- /dev/null
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.decltype/p1.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -std=c++20 -verify %s
+// expected-no-diagnostics
+
+// bullet 2: decltype(x) where x is a non-type template parameter gives the
+// type of X, after deduction, if any.
+namespace ClassNTTP {
+ template<decltype(auto) v, typename ParamT, typename ExprT> void f() {
+ using U = decltype(v);
+ using U = ParamT;
+
+ using V = decltype((v));
+ using V = ExprT;
+ }
+
+ // The names of most non-reference NTTPs are prvalues.
+ template void f<0, int, int>();
+
+ // The name of a class NTTP of type T is an lvalue of type 'const T'.
+ struct X {};
+ template void f<X{}, X, const X&>();
+
+ // Ensure we get this right for references to classes too.
+ template<auto x> auto &TempParamObject = x;
+ template void f<TempParamObject<X{}>, const X&, const X&>();
+
+ struct Y {} y;
+ template void f<(y), Y&, Y&>();
+ template void f<y, Y, const Y&>();
+}
More information about the cfe-commits
mailing list