https://github.com/mizvekov created https://github.com/llvm/llvm-project/pull/160018
This fixes a bunch of bugs with how non-type template parameters are transformed and used in template argument deduction. >From a0db4a10c306786d3ab689c4094a2676c18f2c53 Mon Sep 17 00:00:00 2001 From: Matheus Izvekov <mizve...@gmail.com> Date: Mon, 15 Sep 2025 01:58:01 -0300 Subject: [PATCH] [clang] WIP: implement ConstantTemplateParamCastExpr This fixes a bunch of bugs with how non-type template parameters are transformed and used in template argument deduction. --- clang/include/clang/AST/ComputeDependence.h | 2 + clang/include/clang/AST/Expr.h | 8 +- clang/include/clang/AST/ExprCXX.h | 56 ++++++++++++ clang/include/clang/AST/JSONNodeDumper.h | 2 + clang/include/clang/AST/RecursiveASTVisitor.h | 6 ++ clang/include/clang/AST/Stmt.h | 5 +- clang/include/clang/AST/TextNodeDumper.h | 2 + .../clang/Basic/DiagnosticSemaKinds.td | 3 + clang/include/clang/Basic/StmtNodes.td | 1 + clang/include/clang/Sema/Sema.h | 6 +- .../include/clang/Serialization/ASTBitCodes.h | 3 + clang/lib/AST/ComputeDependence.cpp | 11 +++ clang/lib/AST/Expr.cpp | 1 + clang/lib/AST/ExprCXX.cpp | 24 +++++ clang/lib/AST/ExprClassification.cpp | 1 + clang/lib/AST/ExprConstant.cpp | 1 + clang/lib/AST/ItaniumMangle.cpp | 3 +- clang/lib/AST/JSONNodeDumper.cpp | 6 ++ clang/lib/AST/StmtPrinter.cpp | 6 ++ clang/lib/AST/StmtProfile.cpp | 10 +++ clang/lib/AST/TextNodeDumper.cpp | 8 ++ clang/lib/Sema/SemaExceptionSpec.cpp | 1 + clang/lib/Sema/SemaOverload.cpp | 5 +- clang/lib/Sema/SemaTemplate.cpp | 89 ++++++++++--------- clang/lib/Sema/SemaTemplateDeduction.cpp | 46 ++++------ clang/lib/Sema/SemaTemplateInstantiate.cpp | 53 ++--------- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 23 +++-- clang/lib/Sema/TreeTransform.h | 64 +++++++++++-- clang/lib/Serialization/ASTReaderStmt.cpp | 11 +++ clang/lib/Serialization/ASTWriterStmt.cpp | 7 ++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 2 +- .../SemaCXX/delete-and-function-templates.cpp | 2 +- clang/test/SemaTemplate/make_integer_seq.cpp | 6 +- clang/test/SemaTemplate/temp_arg_nontype.cpp | 2 +- .../SemaTemplate/temp_arg_nontype_cxx2c.cpp | 11 +++ .../SemaTemplate/temp_arg_template_p0522.cpp | 10 +-- clang/tools/libclang/CXCursor.cpp | 1 + 38 files changed, 345 insertions(+), 154 deletions(-) diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index c298f2620f211..7abe33f4d6db7 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -32,6 +32,7 @@ class MatrixSubscriptExpr; class CompoundLiteralExpr; class ImplicitCastExpr; class ExplicitCastExpr; +class ConstantTemplateParamCastExpr; class BinaryOperator; class ConditionalOperator; class BinaryConditionalOperator; @@ -121,6 +122,7 @@ ExprDependence computeDependence(MatrixSubscriptExpr *E); ExprDependence computeDependence(CompoundLiteralExpr *E); ExprDependence computeDependence(ImplicitCastExpr *E); ExprDependence computeDependence(ExplicitCastExpr *E); +ExprDependence computeDependence(ConstantTemplateParamCastExpr *E); ExprDependence computeDependence(BinaryOperator *E); ExprDependence computeDependence(ConditionalOperator *E); ExprDependence computeDependence(BinaryConditionalOperator *E); diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index e1a4005d1a890..2667a942479cb 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -3624,7 +3624,7 @@ class CastExpr : public Expr { Expr *op, unsigned BasePathSize, bool HasFPFeatures) : Expr(SC, ty, VK, OK_Ordinary), Op(op) { CastExprBits.Kind = kind; - CastExprBits.PartOfExplicitCast = false; + CastExprBits.ExtraData = false; CastExprBits.BasePathSize = BasePathSize; assert((CastExprBits.BasePathSize == BasePathSize) && "BasePathSize overflow!"); @@ -3636,7 +3636,7 @@ class CastExpr : public Expr { CastExpr(StmtClass SC, EmptyShell Empty, unsigned BasePathSize, bool HasFPFeatures) : Expr(SC, Empty) { - CastExprBits.PartOfExplicitCast = false; + CastExprBits.ExtraData = false; CastExprBits.BasePathSize = BasePathSize; CastExprBits.HasFPFeatures = HasFPFeatures; assert((CastExprBits.BasePathSize == BasePathSize) && @@ -3815,9 +3815,9 @@ class ImplicitCastExpr final *getTrailingFPFeatures() = FPO; } - bool isPartOfExplicitCast() const { return CastExprBits.PartOfExplicitCast; } + bool isPartOfExplicitCast() const { return CastExprBits.ExtraData; } void setIsPartOfExplicitCast(bool PartOfExplicitCast) { - CastExprBits.PartOfExplicitCast = PartOfExplicitCast; + CastExprBits.ExtraData = PartOfExplicitCast; } static ImplicitCastExpr *Create(const ASTContext &Context, QualType T, diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 9fedb230ce397..325744a649407 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -4653,6 +4653,62 @@ class PackIndexingExpr final } }; +/// ImplicitCastExpr - Allows us to explicitly represent implicit type +/// conversions, which have no direct representation in the original +/// source code. For example: converting T[]->T*, void f()->void +/// (*f)(), float->double, short->int, etc. +class ConstantTemplateParamCastExpr final : public CastExpr { + + NonTypeTemplateParmDecl *Param; + + ConstantTemplateParamCastExpr(NonTypeTemplateParmDecl *Param, QualType ty, + Expr *op, ExprValueKind VK, bool IsDeduced) + : CastExpr(ConstantTemplateParamCastExprClass, ty, VK, + CastKind::CK_Dependent, op, + /*BasePathSize=*/0, + /*HasFPFeatures=*/false), + Param(Param) { + CastExprBits.ExtraData = IsDeduced; + setDependence(computeDependence(this)); + } + + explicit ConstantTemplateParamCastExpr(EmptyShell Shell) + : CastExpr(ConstantTemplateParamCastExprClass, Shell, /*BasePathSize=*/0, + /*HasFPFeatures=*/false) {} + + template <class T> T *getTrailingObjectsNonStrict() { return nullptr; } + +public: + static ConstantTemplateParamCastExpr *Create(const ASTContext &Context, + NonTypeTemplateParmDecl *Param, + QualType ParamType, + Expr *Operand, bool IsDeduced); + + static ConstantTemplateParamCastExpr *CreateEmpty(const ASTContext &Context) { + return new (Context) ConstantTemplateParamCastExpr(EmptyShell()); + } + + NonTypeTemplateParmDecl *getParam() const { return Param; } + + QualType getParamType(const ASTContext &Context) const; + + bool isDeduced() const { return CastExprBits.ExtraData; } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getSubExpr()->getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { + return getSubExpr()->getEndLoc(); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ConstantTemplateParamCastExprClass; + } + + friend class CastExpr; + friend class ASTStmtReader; +}; + /// Represents a reference to a non-type template parameter /// that has been substituted with a template argument. class SubstNonTypeTemplateParmExpr : public Expr { diff --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h index 427a9c51ece1b..7da9eb3f35d36 100644 --- a/clang/include/clang/AST/JSONNodeDumper.h +++ b/clang/include/clang/AST/JSONNodeDumper.h @@ -297,6 +297,8 @@ class JSONNodeDumper void VisitCXXThisExpr(const CXXThisExpr *TE); void VisitCastExpr(const CastExpr *CE); void VisitImplicitCastExpr(const ImplicitCastExpr *ICE); + void + VisitConstantTemplateParamCastExpr(const ConstantTemplateParamCastExpr *CE); void VisitCallExpr(const CallExpr *CE); void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *TTE); void VisitSizeOfPackExpr(const SizeOfPackExpr *SOPE); diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 1d1b7f183f75a..e88f90ff8ed55 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2628,6 +2628,12 @@ DEF_TRAVERSE_STMT(MemberExpr, { S->getNumTemplateArgs())); }) +DEF_TRAVERSE_STMT(ConstantTemplateParamCastExpr, + { + // We don't traverse the cast type, as it's not written in + // the source code. + }) + DEF_TRAVERSE_STMT( ImplicitCastExpr, {// We don't traverse the cast type, as it's not written in the diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 76942f1a84f9a..0dd73f28327bf 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -616,14 +616,17 @@ class alignas(void *) Stmt { class CastExprBitfields { friend class CastExpr; friend class ImplicitCastExpr; + friend class ConstantTemplateParamCastExpr; LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; LLVM_PREFERRED_TYPE(CastKind) unsigned Kind : 7; + + // Used by ImplicitCastExpr and ConstantTemplateParamCastExpr. LLVM_PREFERRED_TYPE(bool) - unsigned PartOfExplicitCast : 1; // Only set for ImplicitCastExpr. + unsigned ExtraData : 1; /// True if the call expression has some floating-point features. LLVM_PREFERRED_TYPE(bool) diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 88ecd526e3d7e..2df912479568b 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -271,6 +271,8 @@ class TextNodeDumper void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Node); void VisitCastExpr(const CastExpr *Node); void VisitImplicitCastExpr(const ImplicitCastExpr *Node); + void + VisitConstantTemplateParamCastExpr(const ConstantTemplateParamCastExpr *Node); void VisitDeclRefExpr(const DeclRefExpr *Node); void VisitDependentScopeDeclRefExpr(const DependentScopeDeclRefExpr *Node); void VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *Node); diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index bd896524321d1..823771657c531 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5774,6 +5774,9 @@ def note_template_class_explicit_specialization_was_here : Note< "class template %0 was explicitly specialized here">; def note_template_class_instantiation_here : Note< "in instantiation of template class %q0 requested here">; +def note_non_type_template_parameter_instantiation_here + : Note< + "in instantiation of non-type template parameter %q0 requested here">; def note_template_member_class_here : Note< "in instantiation of member class %q0 requested here">; def note_template_member_function_here : Note< diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index dd1a24405fae7..f1f063068c728 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -86,6 +86,7 @@ def AbstractConditionalOperator : StmtNode<Expr, 1>; def ConditionalOperator : StmtNode<AbstractConditionalOperator>; def BinaryConditionalOperator : StmtNode<AbstractConditionalOperator>; def ImplicitCastExpr : StmtNode<CastExpr>; +def ConstantTemplateParamCastExpr : StmtNode<CastExpr>; def ExplicitCastExpr : StmtNode<CastExpr, 1>; def CStyleCastExpr : StmtNode<ExplicitCastExpr>; def OMPArrayShapingExpr : StmtNode<Expr>; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d017d1f829015..d409a0a09bed8 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12683,11 +12683,11 @@ class Sema final : public SemaBase { QualType Replacement); // Substitute auto in TypeWithAuto for a Dependent auto type - QualType SubstAutoTypeDependent(QualType TypeWithAuto); + QualType SubstAutoTypeDependent(QualType TypeWithAuto, bool IsPack = false); // Substitute auto in TypeWithAuto for a Dependent auto type - TypeSourceInfo * - SubstAutoTypeSourceInfoDependent(TypeSourceInfo *TypeWithAuto); + TypeSourceInfo *SubstAutoTypeSourceInfoDependent(TypeSourceInfo *TypeWithAuto, + bool IsPack = false); /// Completely replace the \c auto in \p TypeWithAuto by /// \p Replacement. This does not retain any \c auto type sugar. diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 441047d64f48c..8fd9de20c9b77 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1684,6 +1684,9 @@ enum StmtCode { /// An ImplicitCastExpr record. EXPR_IMPLICIT_CAST, + /// An ConstantTemplateParamCastExpr record. + EXPR_CONSTANT_TEMPLATE_PARAM_CAST, + /// A CStyleCastExpr record. EXPR_CSTYLE_CAST, diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index e0cf0deb12bd2..51ef1b4ffb308 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -157,6 +157,17 @@ ExprDependence clang::computeDependence(ExplicitCastExpr *E) { return D; } +ExprDependence clang::computeDependence(ConstantTemplateParamCastExpr *E) { + // We model implicit conversions as combining the dependence of their + // subexpression, apart from its type, with the semantic portion of the + // target type. + ExprDependence D = + toExprDependenceForImpliedType(E->getType()->getDependence()); + if (auto *S = E->getSubExpr()) + D |= S->getDependence() & ~ExprDependence::Type; + return D; +} + ExprDependence clang::computeDependence(BinaryOperator *E) { return E->getLHS()->getDependence() | E->getRHS()->getDependence(); } diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index f899b3c4bb79c..727c89ad8b734 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3858,6 +3858,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, return true; } [[fallthrough]]; + case ConstantTemplateParamCastExprClass: case ImplicitCastExprClass: case CStyleCastExprClass: case CXXStaticCastExprClass: diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 97ae4a07f32aa..c5f3ae8c17b83 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1765,6 +1765,30 @@ PackIndexingExpr::CreateDeserialized(ASTContext &Context, return new (Storage) PackIndexingExpr(EmptyShell{}); } +ConstantTemplateParamCastExpr *ConstantTemplateParamCastExpr::Create( + const ASTContext &C, NonTypeTemplateParmDecl *Param, QualType ParamType, + Expr *Operand, bool IsDeduced) { + return new (C) ConstantTemplateParamCastExpr( + Param, ParamType.getNonLValueExprType(C), Operand, + ParamType->isLValueReferenceType() ? VK_LValue + : ParamType->isRValueReferenceType() ? VK_XValue + : VK_PRValue, + IsDeduced); +} + +QualType +ConstantTemplateParamCastExpr::getParamType(const ASTContext &C) const { + switch (getValueKind()) { + case VK_LValue: + return C.getLValueReferenceType(getType()); + case VK_XValue: + return C.getRValueReferenceType(getType()); + case VK_PRValue: + return getType(); + } + llvm_unreachable("unhandled value kind"); +} + QualType SubstNonTypeTemplateParmExpr::getParameterType( const ASTContext &Context) const { // Note that, for a class type NTTP, we will have an lvalue of type 'const diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index aeacd0dc765ef..489584bb3ebf9 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -322,6 +322,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { // Implicit casts are lvalues if they're lvalue casts. Other than that, we // only specifically record class temporaries. + case Expr::ConstantTemplateParamCastExprClass: case Expr::ImplicitCastExprClass: return ClassifyExprValueKind(Lang, E, E->getValueKind()); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 3b9ca82910033..da3bdddfa59a3 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -18341,6 +18341,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { } llvm_unreachable("invalid binary operator kind"); } + case Expr::ConstantTemplateParamCastExprClass: case Expr::ImplicitCastExprClass: case Expr::CStyleCastExprClass: case Expr::CXXFunctionalCastExprClass: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 2173aed5b45af..1e237dbb29e61 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -5527,9 +5527,10 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, break; } + case Expr::ConstantTemplateParamCastExprClass: case Expr::ImplicitCastExprClass: { ImplicitlyConvertedToType = E->getType(); - E = cast<ImplicitCastExpr>(E)->getSubExpr(); + E = cast<CastExpr>(E)->getSubExpr(); goto recurse; } diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index 0ef632805d67c..3a50d1fc493d1 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -1459,6 +1459,12 @@ void JSONNodeDumper::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) { attributeOnlyIfTrue("isPartOfExplicitCast", ICE->isPartOfExplicitCast()); } +void JSONNodeDumper::VisitConstantTemplateParamCastExpr( + const ConstantTemplateParamCastExpr *CE) { + VisitCastExpr(CE); + JOS.attribute("param", createBareDeclRef(CE->getParam())); +} + void JSONNodeDumper::VisitCallExpr(const CallExpr *CE) { attributeOnlyIfTrue("adl", CE->usesADL()); } diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 2c9c3581a2962..5eb5202138686 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1822,6 +1822,12 @@ void StmtPrinter::VisitCompoundLiteralExpr(CompoundLiteralExpr *Node) { PrintExpr(Node->getInitializer()); } +void StmtPrinter::VisitConstantTemplateParamCastExpr( + ConstantTemplateParamCastExpr *Node) { + // No need to print anything, simply forward to the subexpression. + PrintExpr(Node->getSubExpr()); +} + void StmtPrinter::VisitImplicitCastExpr(ImplicitCastExpr *Node) { // No need to print anything, simply forward to the subexpression. PrintExpr(Node->getSubExpr()); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 37c4d43ec0b2f..65d1cdb7cf87b 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1519,6 +1519,16 @@ void StmtProfiler::VisitCastExpr(const CastExpr *S) { VisitExpr(S); } +void StmtProfiler::VisitConstantTemplateParamCastExpr( + const ConstantTemplateParamCastExpr *S) { + if (S->isDeduced()) { + Visit(S->getSubExpr()); + } else { + VisitCastExpr(S); + VisitDecl(S->getParam()); + } +} + void StmtProfiler::VisitImplicitCastExpr(const ImplicitCastExpr *S) { VisitCastExpr(S); ID.AddInteger(S->getValueKind()); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 8f7fe3bea4e8f..3b6c7f8613efb 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1534,6 +1534,14 @@ void TextNodeDumper::VisitImplicitCastExpr(const ImplicitCastExpr *Node) { OS << " part_of_explicit_cast"; } +void TextNodeDumper::VisitConstantTemplateParamCastExpr( + const ConstantTemplateParamCastExpr *Node) { + VisitCastExpr(Node); + if (Node->isDeduced()) + OS << " is_deduced"; + dumpDeclRef(Node->getParam()); +} + void TextNodeDumper::VisitDeclRefExpr(const DeclRefExpr *Node) { OS << " "; dumpBareDeclRef(Node->getDecl()); diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 552c92996dc2e..d384907f0ee3a 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1312,6 +1312,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::CStyleCastExprClass: case Expr::CXXStaticCastExprClass: case Expr::CXXFunctionalCastExprClass: + case Expr::ConstantTemplateParamCastExprClass: case Expr::ImplicitCastExprClass: case Expr::MaterializeTemporaryExprClass: case Expr::UnaryOperatorClass: { diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index ea5c4265d736d..b2a3da42257e6 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -14744,10 +14744,13 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl); if (Res.isInvalid()) return ExprError(); - return SemaRef.BuildResolvedCallExpr( + Res = SemaRef.BuildResolvedCallExpr( Res.get(), FDecl, LParenLoc, Args, RParenLoc, ExecConfig, /*IsExecConfig=*/false, static_cast<CallExpr::ADLCallKind>((*Best)->IsADLCandidate)); + if (Res.isInvalid()) + return ExprError(); + Fn = Res.get(); } } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index f051a246f954f..b5b0cbcc680b8 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5474,7 +5474,18 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc, const TemplateArgument &Arg = ArgLoc.getArgument(); // Check non-type template parameters. - if (NonTypeTemplateParmDecl *NTTP =dyn_cast<NonTypeTemplateParmDecl>(Param)) { + if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) { + if (Arg.getKind() == TemplateArgument::Expression) + if (auto *E = dyn_cast<ConstantTemplateParamCastExpr>(Arg.getAsExpr()); + E && E->getParam() == Param) { + // The argument is already converted for this parameter. + CTAI.SugaredConverted.push_back( + TemplateArgument(E, /*IsCanonical=*/false)); + CTAI.CanonicalConverted.push_back( + TemplateArgument(E, /*IsCanonical=*/true)); + return false; + } + // Do substitution on the type of the non-type template parameter // with the template arguments we've seen thus far. But if the // template has a dependent context then we cannot substitute yet. @@ -5644,7 +5655,6 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc, return false; } - // Check template template parameters. TemplateTemplateParmDecl *TempParm = cast<TemplateTemplateParmDecl>(Param); @@ -7068,25 +7078,13 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType, // If the parameter type somehow involves auto, deduce the type now. DeducedType *DeducedT = ParamType->getContainedDeducedType(); + bool IsDeduced = false; if (getLangOpts().CPlusPlus17 && DeducedT && !DeducedT->isDeduced()) { - // During template argument deduction, we allow 'decltype(auto)' to - // match an arbitrary dependent argument. - // FIXME: The language rules don't say what happens in this case. - // FIXME: We get an opaque dependent type out of decltype(auto) if the - // expression is merely instantiation-dependent; is this enough? - if (DeductionArg->isTypeDependent()) { - auto *AT = dyn_cast<AutoType>(DeducedT); - if (AT && AT->isDecltypeAuto()) { - SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false); - CanonicalConverted = TemplateArgument( - Context.getCanonicalTemplateArgument(SugaredConverted)); - return Arg; - } - } - - // When checking a deduced template argument, deduce from its type even if - // the type is dependent, in order to check the types of non-type template - // arguments line up properly in partial ordering. + IsDeduced = true; + // QualType UndeducedParamType = ParamType; + // When checking a deduced template argument, deduce from its type even if + // the type is dependent, in order to check the types of non-type template + // arguments line up properly in partial ordering. TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(ParamType, Param->getLocation()); if (isa<DeducedTemplateSpecializationType>(DeducedT)) { @@ -7112,17 +7110,22 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType, // along with the other associated constraints after // checking the template argument list. /*IgnoreConstraints=*/true); - if (Result == TemplateDeductionResult::AlreadyDiagnosed) { - return ExprError(); - } else if (Result != TemplateDeductionResult::Success) { - if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) { - Diag(Arg->getExprLoc(), - diag::err_non_type_template_parm_type_deduction_failure) - << Param->getDeclName() << NTTP->getType() << Arg->getType() - << Arg->getSourceRange(); + if (Result != TemplateDeductionResult::Success) { + ParamType = TSI->getType(); + if (StrictCheck || !DeductionArg->isTypeDependent()) { + if (Result == TemplateDeductionResult::AlreadyDiagnosed) + return ExprError(); + if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) + Diag(Arg->getExprLoc(), + diag::err_non_type_template_parm_type_deduction_failure) + << Param->getDeclName() << NTTP->getType() << Arg->getType() + << Arg->getSourceRange(); + NoteTemplateParameterLocation(*Param); + return ExprError(); } - NoteTemplateParameterLocation(*Param); - return ExprError(); + ParamType = SubstAutoTypeDependent( + ParamType, /*IsPack=*/isa<PackExpansionType>(ParamType)); + assert(!ParamType.isNull() && "substituting DependentTy can't fail"); } } // CheckNonTypeTemplateParameterType will produce a diagnostic if there's @@ -7144,14 +7147,11 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType, // type-dependent, there's nothing we can check now. if (ParamType->isDependentType() || DeductionArg->isTypeDependent()) { // Force the argument to the type of the parameter to maintain invariants. - ExprResult E = ImpCastExprToType( - DeductionArg, ParamType.getNonLValueExprType(Context), CK_Dependent, - ParamType->isLValueReferenceType() ? VK_LValue - : ParamType->isRValueReferenceType() ? VK_XValue - : VK_PRValue); - if (E.isInvalid()) - return ExprError(); - setDeductionArg(E.get()); + if (!Context.hasSameType(DeductionArg->getType(), ParamType)) + DeductionArg = ConstantTemplateParamCastExpr::Create( + Context, cast<NonTypeTemplateParmDecl>(Param), ParamType, + DeductionArg, IsDeduced); + setDeductionArg(DeductionArg); SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false); CanonicalConverted = TemplateArgument( Context.getCanonicalTemplateArgument(SugaredConverted)); @@ -8555,6 +8555,7 @@ static SourceRange findTemplateParameter(unsigned Depth, TypeLoc TL) { static bool CheckNonTypeTemplatePartialSpecializationArgs( Sema &S, SourceLocation TemplateNameLoc, NonTypeTemplateParmDecl *Param, const TemplateArgument *Args, unsigned NumArgs, bool IsDefaultArgument) { + bool HasError = false; for (unsigned I = 0; I != NumArgs; ++I) { if (Args[I].getKind() == TemplateArgument::Pack) { if (CheckNonTypeTemplatePartialSpecializationArgs( @@ -8574,8 +8575,9 @@ static bool CheckNonTypeTemplatePartialSpecializationArgs( if (PackExpansionExpr *Expansion = dyn_cast<PackExpansionExpr>(ArgExpr)) ArgExpr = Expansion->getPattern(); - // Strip off any implicit casts we added as part of type checking. - while (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(ArgExpr)) + // Strip off any constant template parameter casts we added when checking + // the template argument. + while (auto *ICE = dyn_cast<ConstantTemplateParamCastExpr>(ArgExpr)) ArgExpr = ICE->getSubExpr(); // C++ [temp.class.spec]p8: @@ -8595,6 +8597,11 @@ static bool CheckNonTypeTemplatePartialSpecializationArgs( continue; } + if (isa<RecoveryExpr>(ArgExpr)) { + HasError = true; + continue; + } + // C++ [temp.class.spec]p9: // Within the argument list of a class template partial // specialization, the following restrictions apply: @@ -8638,7 +8645,7 @@ static bool CheckNonTypeTemplatePartialSpecializationArgs( } } - return false; + return HasError; } bool Sema::CheckTemplatePartialSpecializationArgs( diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 62e867c44ad14..47bc57c166ebc 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -153,6 +153,8 @@ static const Expr *unwrapExpressionForDeduction(const Expr *E) { while (true) { if (const auto *IC = dyn_cast<ImplicitCastExpr>(E)) E = IC->getSubExpr(); + if (const auto *IC = dyn_cast<ConstantTemplateParamCastExpr>(E)) + E = IC->getSubExpr(); else if (const auto *CE = dyn_cast<ConstantExpr>(E)) E = CE->getSubExpr(); else if (const auto *Subst = dyn_cast<SubstNonTypeTemplateParmExpr>(E)) @@ -2659,12 +2661,13 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // cast, in order to maintain invariants. Now we can deduce the // resulting type from the original type, and deduce the original type // against the parameter we are checking. - if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E); - ICE && ICE->getCastKind() == clang::CK_Dependent) { - E = ICE->getSubExpr(); + if (const auto *CE = dyn_cast<ConstantTemplateParamCastExpr>(E); + CE && !CE->isDeduced()) { + E = CE->getSubExpr(); + assert(!isa<ConstantTemplateParamCastExpr>(E)); if (auto Result = DeduceTemplateArgumentsByTypeMatch( - S, TemplateParams, ICE->getType(), E->getType(), Info, - Deduced, TDF_SkipNonDependent, + S, TemplateParams, CE->getType(), E->getType(), Info, Deduced, + TDF_SkipNonDependent, PartialOrdering ? PartialOrderingKind::NonCall : PartialOrderingKind::None, /*DeducedFromArrayBound=*/false, HasDeducedAnyParam); @@ -5279,18 +5282,6 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, SmallVector<DeducedTemplateArgument, 1> Deduced; Deduced.resize(1); - // If deduction failed, don't diagnose if the initializer is dependent; it - // might acquire a matching type in the instantiation. - auto DeductionFailed = [&](TemplateDeductionResult TDK) { - if (Init->isTypeDependent()) { - Result = - SubstituteDeducedTypeTransform(*this, DependentResult).Apply(Type); - assert(!Result.isNull() && "substituting DependentTy can't fail"); - return TemplateDeductionResult::Success; - } - return TDK; - }; - SmallVector<OriginalCallArg, 4> OriginalCallArgs; QualType DeducedType; @@ -5340,9 +5331,9 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, Diag(Info.getLocation(), diag::err_auto_inconsistent_deduction) << Info.FirstArg << Info.SecondArg << DeducedFromInitRange << Init->getSourceRange(); - return DeductionFailed(TemplateDeductionResult::AlreadyDiagnosed); + return TemplateDeductionResult::AlreadyDiagnosed; } - return DeductionFailed(TDK); + return TDK; } if (DeducedFromInitRange.isInvalid() && @@ -5364,12 +5355,12 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, OriginalCallArgs, /*Decomposed=*/false, /*ArgIdx=*/0, /*TDF=*/0, FailedTSC); TDK != TemplateDeductionResult::Success) - return DeductionFailed(TDK); + return TDK; } // Could be null if somehow 'auto' appears in a non-deduced context. if (Deduced[0].getKind() != TemplateArgument::Type) - return DeductionFailed(TemplateDeductionResult::Incomplete); + return TemplateDeductionResult::Incomplete; DeducedType = Deduced[0].getAsType(); if (InitList) { @@ -5383,7 +5374,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, if (!Context.hasSameType(DeducedType, Result)) { Info.FirstArg = Result; Info.SecondArg = DeducedType; - return DeductionFailed(TemplateDeductionResult::Inconsistent); + return TemplateDeductionResult::Inconsistent; } DeducedType = Context.getCommonSugaredType(Result, DeducedType); } @@ -5407,7 +5398,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, CheckOriginalCallArgDeduction(*this, Info, OriginalArg, DeducedA); TDK != TemplateDeductionResult::Success) { Result = QualType(); - return DeductionFailed(TDK); + return TDK; } } @@ -5428,14 +5419,15 @@ TypeSourceInfo *Sema::SubstAutoTypeSourceInfo(TypeSourceInfo *TypeWithAuto, .TransformType(TypeWithAuto); } -QualType Sema::SubstAutoTypeDependent(QualType TypeWithAuto) { - return SubstituteDeducedTypeTransform(*this, DependentAuto{false}) +QualType Sema::SubstAutoTypeDependent(QualType TypeWithAuto, bool IsPack) { + return SubstituteDeducedTypeTransform(*this, DependentAuto{IsPack}) .TransformType(TypeWithAuto); } TypeSourceInfo * -Sema::SubstAutoTypeSourceInfoDependent(TypeSourceInfo *TypeWithAuto) { - return SubstituteDeducedTypeTransform(*this, DependentAuto{false}) +Sema::SubstAutoTypeSourceInfoDependent(TypeSourceInfo *TypeWithAuto, + bool IsPack) { + return SubstituteDeducedTypeTransform(*this, DependentAuto{IsPack}) .TransformType(TypeWithAuto); } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index a72c95d6d77cf..6996437ab6b0e 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -988,6 +988,11 @@ void Sema::PrintInstantiationStack(InstantiationContextDiagFuncRef DiagFunc) { DiagFunc(Active->PointOfInstantiation, PDiag(diag::note_template_class_instantiation_here) << CTD << Active->InstantiationRange); + } else if (auto *NTTPD = dyn_cast<NonTypeTemplateParmDecl>(D)) { + DiagFunc( + Active->PointOfInstantiation, + PDiag(diag::note_non_type_template_parameter_instantiation_here) + << NTTPD << Active->InstantiationRange); } break; } @@ -1644,9 +1649,7 @@ namespace { ExprResult TransformTemplateParmRefExpr(DeclRefExpr *E, NonTypeTemplateParmDecl *D); ExprResult TransformSubstNonTypeTemplateParmPackExpr( - SubstNonTypeTemplateParmPackExpr *E); - ExprResult TransformSubstNonTypeTemplateParmExpr( - SubstNonTypeTemplateParmExpr *E); + SubstNonTypeTemplateParmPackExpr *E); /// Rebuild a DeclRefExpr for a VarDecl reference. ExprResult RebuildVarDeclRefExpr(ValueDecl *PD, SourceLocation Loc); @@ -2438,50 +2441,6 @@ TemplateInstantiator::TransformSubstNonTypeTemplateParmPackExpr( E->getParameterPackLocation(), Arg, getPackIndex(Pack), E->getFinal()); } -ExprResult -TemplateInstantiator::TransformSubstNonTypeTemplateParmExpr( - SubstNonTypeTemplateParmExpr *E) { - ExprResult SubstReplacement = E->getReplacement(); - if (!isa<ConstantExpr>(SubstReplacement.get())) - SubstReplacement = TransformExpr(E->getReplacement()); - if (SubstReplacement.isInvalid()) - return true; - QualType SubstType = TransformType(E->getParameterType(getSema().Context)); - if (SubstType.isNull()) - return true; - // The type may have been previously dependent and not now, which means we - // might have to implicit cast the argument to the new type, for example: - // template<auto T, decltype(T) U> - // concept C = sizeof(U) == 4; - // void foo() requires C<2, 'a'> { } - // When normalizing foo(), we first form the normalized constraints of C: - // AtomicExpr(sizeof(U) == 4, - // U=SubstNonTypeTemplateParmExpr(Param=U, - // Expr=DeclRef(U), - // Type=decltype(T))) - // Then we substitute T = 2, U = 'a' into the parameter mapping, and need to - // produce: - // AtomicExpr(sizeof(U) == 4, - // U=SubstNonTypeTemplateParmExpr(Param=U, - // Expr=ImpCast( - // decltype(2), - // SubstNTTPE(Param=U, Expr='a', - // Type=char)), - // Type=decltype(2))) - // The call to CheckTemplateArgument here produces the ImpCast. - TemplateArgument SugaredConverted, CanonicalConverted; - if (SemaRef - .CheckTemplateArgument(E->getParameter(), SubstType, - SubstReplacement.get(), SugaredConverted, - CanonicalConverted, - /*StrictCheck=*/false, Sema::CTAK_Specified) - .isInvalid()) - return true; - return transformNonTypeTemplateParmRef( - E->getAssociatedDecl(), E->getParameter(), E->getExprLoc(), - SugaredConverted, E->getPackIndex(), E->getFinal()); -} - ExprResult TemplateInstantiator::RebuildVarDeclRefExpr(ValueDecl *PD, SourceLocation Loc) { DeclarationNameInfo NameInfo(PD->getDeclName(), Loc); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index adac3dff5b2b4..e2dc70360506e 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3742,7 +3742,7 @@ TemplateDeclInstantiator::VisitTemplateTemplateParmDecl( ExpandedParams.reserve(D->getNumExpansionTemplateParameters()); for (unsigned I = 0, N = D->getNumExpansionTemplateParameters(); I != N; ++I) { - LocalInstantiationScope Scope(SemaRef); + LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); TemplateParameterList *Expansion = SubstTemplateParams(D->getExpansionTemplateParameters(I)); if (!Expansion) @@ -3774,7 +3774,7 @@ TemplateDeclInstantiator::VisitTemplateTemplateParmDecl( if (Expand) { for (unsigned I = 0; I != *NumExpansions; ++I) { Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, I); - LocalInstantiationScope Scope(SemaRef); + LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); TemplateParameterList *Expansion = SubstTemplateParams(TempParams); if (!Expansion) return nullptr; @@ -3785,21 +3785,18 @@ TemplateDeclInstantiator::VisitTemplateTemplateParmDecl( // expanded parameter pack is the original expansion type, but callers // will end up using the expanded parameter pack types for type-checking. IsExpandedParameterPack = true; - InstParams = TempParams; - } else { - // We cannot fully expand the pack expansion now, so just substitute - // into the pattern. - Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, std::nullopt); - - LocalInstantiationScope Scope(SemaRef); - InstParams = SubstTemplateParams(TempParams); - if (!InstParams) - return nullptr; } + + Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, std::nullopt); + + LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); + InstParams = SubstTemplateParams(TempParams); + if (!InstParams) + return nullptr; } else { // Perform the actual substitution of template parameters within a new, // local instantiation scope. - LocalInstantiationScope Scope(SemaRef); + LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); InstParams = SubstTemplateParams(TempParams); if (!InstParams) return nullptr; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 242ffb09af006..006168af0ab0e 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -13650,6 +13650,43 @@ TreeTransform<Derived>::TransformConditionalOperator(ConditionalOperator *E) { RHS.get()); } +template <typename Derived> +ExprResult TreeTransform<Derived>::TransformConstantTemplateParamCastExpr( + ConstantTemplateParamCastExpr *E) { + ExprResult SubExpr = getDerived().TransformExpr(E->getSubExpr()); + if (SubExpr.isInvalid()) + return true; + + auto *Param = cast_or_null<NonTypeTemplateParmDecl>( + getDerived().TransformDecl(E->getExprLoc(), E->getParam())); + if (!Param) + return true; + + SourceLocation ParamLoc = Param->getLocation(); + + QualType ParamType = + E->isDeduced() ? Param->getType() : E->getParamType(SemaRef.Context); + if (!getDerived().AlreadyTransformed(ParamType)) { + Sema::InstantiatingTemplate Inst(SemaRef, getDerived().getBaseLocation(), + Param, E->getSourceRange()); + TypeSourceInfo *DI = + E->isDeduced() + ? Param->getTypeSourceInfo() + : SemaRef.Context.getTrivialTypeSourceInfo(ParamType, ParamLoc); + TypeLocBuilder TLB; + ParamType = getDerived().TransformType(TLB, DI->getTypeLoc()); + if (ParamType.isNull()) + return true; + } + ParamType = SemaRef.CheckNonTypeTemplateParameterType(ParamType, ParamLoc); + if (ParamType.isNull()) + return true; + TemplateArgument SugaredConverted, CanonicalConverted; + return SemaRef.CheckTemplateArgument( + Param, ParamType, SubExpr.get(), SugaredConverted, CanonicalConverted, + /*StrictCheck=*/false, Sema::CTAK_Specified); +} + template<typename Derived> ExprResult TreeTransform<Derived>::TransformImplicitCastExpr(ImplicitCastExpr *E) { @@ -16297,12 +16334,27 @@ TreeTransform<Derived>::TransformSubstNonTypeTemplateParmPackExpr( return E; } -template<typename Derived> -ExprResult -TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr( - SubstNonTypeTemplateParmExpr *E) { - // Default behavior is to do nothing with this transformation. - return E; +template <typename Derived> +ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr( + SubstNonTypeTemplateParmExpr *E) { + ExprResult Res = getDerived().TransformExpr(E->getReplacement()); + if (Res.isInvalid()) + return true; + Expr *Replacement = Res.get(); + + Decl *AssociatedDecl = + getDerived().TransformDecl(E->getNameLoc(), E->getAssociatedDecl()); + if (!AssociatedDecl) + return true; + + if (Replacement == E->getReplacement() && + AssociatedDecl == E->getAssociatedDecl()) + return E; + + return new (SemaRef.Context) SubstNonTypeTemplateParmExpr( + Replacement->getType(), Replacement->getValueKind(), E->getNameLoc(), + Replacement, AssociatedDecl, E->getIndex(), E->getPackIndex(), + E->isReferenceParameter(), E->getFinal()); } template<typename Derived> diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 213c2c2148f64..3c3860407446b 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1182,6 +1182,12 @@ ASTStmtReader::VisitBinaryConditionalOperator(BinaryConditionalOperator *E) { E->ColonLoc = readSourceLocation(); } +void ASTStmtReader::VisitConstantTemplateParamCastExpr( + ConstantTemplateParamCastExpr *E) { + VisitCastExpr(E); + E->Param = readDeclAs<NonTypeTemplateParmDecl>(); +} + void ASTStmtReader::VisitImplicitCastExpr(ImplicitCastExpr *E) { VisitCastExpr(E); E->setIsPartOfExplicitCast(CurrentUnpackingBits->getNextBit()); @@ -3327,6 +3333,11 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { break; } + case EXPR_CONSTANT_TEMPLATE_PARAM_CAST: { + S = ConstantTemplateParamCastExpr::CreateEmpty(Context); + break; + } + case EXPR_CSTYLE_CAST: { unsigned PathSize = Record[ASTStmtReader::NumExprFields]; BitsUnpacker CastExprBits(Record[ASTStmtReader::NumExprFields + 1]); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 21c04ddbc2c7a..7785e602dce48 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1149,6 +1149,13 @@ void ASTStmtWriter::VisitImplicitCastExpr(ImplicitCastExpr *E) { Code = serialization::EXPR_IMPLICIT_CAST; } +void ASTStmtWriter::VisitConstantTemplateParamCastExpr( + ConstantTemplateParamCastExpr *E) { + VisitCastExpr(E); + Record.AddDeclRef(E->getParam()); + Code = serialization::EXPR_CONSTANT_TEMPLATE_PARAM_CAST; +} + void ASTStmtWriter::VisitExplicitCastExpr(ExplicitCastExpr *E) { VisitCastExpr(E); Record.AddTypeSourceInfo(E->getTypeInfoAsWritten()); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 785cdfa15bf04..31ea7113a9021 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2280,6 +2280,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; + case Stmt::ConstantTemplateParamCastExprClass: case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: case Stmt::CXXStaticCastExprClass: diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index 1f4d44218ad1f..2f1817d0ca7eb 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -113,7 +113,7 @@ using Bar = Foo<X, sizeof(X)>; // expected-note {{candidate template ignored: co // expected-note {{implicit deduction guide declared as 'template <typename X> requires __is_deducible(test9::Bar, test9::Foo<X, sizeof(X)>) Bar(test9::Foo<X, sizeof(X)>) -> test9::Foo<X, sizeof(X)>'}} \ // expected-note {{implicit deduction guide declared as 'template <typename X> requires __is_deducible(test9::Bar, test9::Foo<X, sizeof(X)>) Bar(const X (&)[sizeof(X)]) -> test9::Foo<X, sizeof(X)>'}} \ // expected-note {{candidate template ignored: constraints not satisfied [with X = int]}} \ - // expected-note {{cannot deduce template arguments for 'test9::Bar' from 'test9::Foo<int, 4UL>'}} + // expected-note {{cannot deduce template arguments for 'test9::Bar' from 'test9::Foo<int, sizeof(int)>'}} Bar s = {{1}}; // expected-error {{no viable constructor or deduction guide }} diff --git a/clang/test/SemaCXX/delete-and-function-templates.cpp b/clang/test/SemaCXX/delete-and-function-templates.cpp index b5854d3073c03..77ac9e9ac24dd 100644 --- a/clang/test/SemaCXX/delete-and-function-templates.cpp +++ b/clang/test/SemaCXX/delete-and-function-templates.cpp @@ -12,7 +12,7 @@ namespace ns1 { template<class T> double f(T) = delete; //expected-note{{candidate}} char f(...); //expected-note{{candidate}} -static_assert(is_same<decltype(f(3)),char>::value, ""); //expected-error{{call to deleted function}} expected-error{{static assertion failed}} +static_assert(is_same<decltype(f(3)),char>::value, ""); //expected-error{{call to deleted function}} template<class T> decltype(f(T{})) g(T); // this one sfinae's out. template<class T> int *g(T); diff --git a/clang/test/SemaTemplate/make_integer_seq.cpp b/clang/test/SemaTemplate/make_integer_seq.cpp index 1203a5812b991..9354b007e9475 100644 --- a/clang/test/SemaTemplate/make_integer_seq.cpp +++ b/clang/test/SemaTemplate/make_integer_seq.cpp @@ -80,7 +80,8 @@ template <template <class T, T...> class S, class T, int N> struct C { // CHECK-NEXT: | `-TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'T' dependent depth 0 index 1 // CHECK-NEXT: | `-TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'T' // CHECK-NEXT: `-TemplateArgument expr 'N' -// CHECK-NEXT: `-ImplicitCastExpr 0x{{[0-9A-Fa-f]+}} <line:{{.+}}:42> 'T' <Dependent> +// CHECK-NEXT: `-ConstantTemplateParamCastExpr 0x{{[0-9A-Fa-f]+}} <line:{{.+}}:42> 'T' <Dependent> +// CHECK-NEXT: |-NonTypeTemplateParm 0x{{[0-9A-Fa-f]+}} depth 0 index 2 'type-parameter-0-1' // CHECK-NEXT: `-DeclRefExpr 0x{{[0-9A-Fa-f]+}} <col:42> 'int' NonTypeTemplateParm 0x{{[0-9A-Fa-f]+}} 'N' 'int' using test4 = __make_integer_seq<A, T, 1>; @@ -94,7 +95,8 @@ template <template <class T, T...> class S, class T, int N> struct C { // CHECK-NEXT: | `-TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'T' dependent depth 0 index 1 // CHECK-NEXT: | `-TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'T' // CHECK-NEXT: `-TemplateArgument expr '1' -// CHECK-NEXT: `-ImplicitCastExpr 0x{{[0-9A-Fa-f]+}} <line:{{.+}}:42> 'T' <Dependent> +// CHECK-NEXT: `-ConstantTemplateParamCastExpr 0x{{[0-9A-Fa-f]+}} <line:{{.+}}:42> 'T' <Dependent> +// CHECK-NEXT: |-NonTypeTemplateParm 0x{{[0-9A-Fa-f]+}} depth 0 index 2 'type-parameter-0-1' // CHECK-NEXT: `-IntegerLiteral 0x{{[0-9A-Fa-f]+}} <col:42> 'int' 1 using test5 = __make_integer_seq<A, int, N>; diff --git a/clang/test/SemaTemplate/temp_arg_nontype.cpp b/clang/test/SemaTemplate/temp_arg_nontype.cpp index 7d2a010295b47..403018ccc5afe 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype.cpp @@ -498,7 +498,7 @@ namespace dependent_backreference { int arr[sizeof(int)]; // When checking this template-id, we must not treat 'Value' as having type // 'int'; its type is the dependent type 'T'. - template<typename T> void f() { X<T, 0, &arr> x; } // expected-note {{substituting}} + template<typename T> void f() { X<T, 0, &arr> x; } // expected-note {{instantiation of non-type template parameter}} void g() { f<short>(); } void h() { f<int>(); } // expected-note {{instantiation}} diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp index e74c031eba4c1..d5dc2720b6413 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp @@ -123,3 +123,14 @@ Set<float> sf; // expected-note@#C {{evaluated to false}} } // namespace GH84052 + +namespace error_on_type_instantiation { + int f(int) = delete; + // expected-note@-1 {{candidate function has been explicitly deleted}} + template<class T, decltype(f(T()))> struct X {}; + // expected-error@-1 {{call to deleted function 'f'}} + template<class T> void g() { X<T, 0> x; } + // expected-note@-1 {{instantiation of non-type template parameter}} + template void g<int>(); + // expected-note@-1 {{in instantiation of function template specialization}} +} diff --git a/clang/test/SemaTemplate/temp_arg_template_p0522.cpp b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp index d8a81bb363112..60d98a653ff02 100644 --- a/clang/test/SemaTemplate/temp_arg_template_p0522.cpp +++ b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp @@ -83,11 +83,11 @@ namespace DependentType { namespace Auto { template<template<int> typename T> struct TInt {}; // #TInt template<template<int*> typename T> struct TIntPtr {}; // #TIntPtr - template<template<auto> typename T> struct TAuto {}; + template<template<auto> typename T> struct TAuto {}; // #TAuto template<template<auto*> typename T> struct TAutoPtr {}; - template<template<decltype(auto)> typename T> struct TDecltypeAuto {}; + template<template<decltype(auto)> typename T> struct TDecltypeAuto {}; // #TDecltypeAuto template<auto> struct Auto; - template<auto*> struct AutoPtr; // #AutoPtr + template<auto*> struct AutoPtr; template<decltype(auto)> struct DecltypeAuto; template<int> struct Int; template<int*> struct IntPtr; @@ -108,7 +108,7 @@ namespace Auto { TIntPtr<IntPtr> ipip; TAuto<Auto> aa; - TAuto<AutoPtr> aap; // expected-error@#AutoPtr {{could not match 'auto *' against 'auto'}} + TAuto<AutoPtr> aap; // expected-error@#TAuto {{non-type template parameter '' with type 'auto *' has incompatible initializer of type 'auto'}} // expected-note@-1 {{different template parameters}} TAuto<Int> ai; // FIXME: ill-formed (?) TAuto<IntPtr> aip; // FIXME: ill-formed (?) @@ -130,7 +130,7 @@ namespace Auto { // parameters (such as 'user-defined-type &') that are not valid 'auto' // parameters. TDecltypeAuto<Auto> daa; - TDecltypeAuto<AutoPtr> daap; // expected-error@#AutoPtr {{could not match 'auto *' against 'decltype(auto)'}} + TDecltypeAuto<AutoPtr> daap; // expected-error@#TDecltypeAuto {{non-type template parameter '' with type 'auto *' has incompatible initializer of type 'decltype(auto)'}} // expected-note@-1 {{different template parameters}} int n; diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index 3c4062410eac1..8999e3589363d 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -318,6 +318,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::ExpressionTraitExprClass: case Stmt::ExtVectorElementExprClass: case Stmt::ImplicitCastExprClass: + case Stmt::ConstantTemplateParamCastExprClass: case Stmt::ImplicitValueInitExprClass: case Stmt::NoInitExprClass: case Stmt::MaterializeTemporaryExprClass: _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits