diff options
Diffstat (limited to 'clang/lib')
71 files changed, 3325 insertions, 700 deletions
diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp index fc32e768d92f8..c28a06bdf0b24 100644 --- a/clang/lib/AST/ASTConcept.cpp +++ b/clang/lib/AST/ASTConcept.cpp @@ -14,6 +14,10 @@ #include "clang/AST/ASTConcept.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/TemplateBase.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/FoldingSet.h" using namespace clang; ASTConstraintSatisfaction::ASTConstraintSatisfaction(const ASTContext &C, @@ -53,3 +57,12 @@ ASTConstraintSatisfaction::Create(const ASTContext &C, void *Mem = C.Allocate(size, alignof(ASTConstraintSatisfaction)); return new (Mem) ASTConstraintSatisfaction(C, Satisfaction); } + +void ConstraintSatisfaction::Profile( + llvm::FoldingSetNodeID &ID, const ASTContext &C, NamedDecl *ConstraintOwner, + ArrayRef<TemplateArgument> TemplateArgs) { + ID.AddPointer(ConstraintOwner); + ID.AddInteger(TemplateArgs.size()); + for (auto &Arg : TemplateArgs) + Arg.Profile(ID, C); +} diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index a51429264dbee..6d1db38e36ccd 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -716,6 +716,61 @@ ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID, RequiresClause->Profile(ID, C, /*Canonical=*/true); } +static Expr * +canonicalizeImmediatelyDeclaredConstraint(const ASTContext &C, Expr *IDC, + QualType ConstrainedType) { + // This is a bit ugly - we need to form a new immediately-declared + // constraint that references the new parameter; this would ideally + // require semantic analysis (e.g. template<C T> struct S {}; - the + // converted arguments of C<T> could be an argument pack if C is + // declared as template<typename... T> concept C = ...). + // We don't have semantic analysis here so we dig deep into the + // ready-made constraint expr and change the thing manually. + ConceptSpecializationExpr *CSE; + if (const auto *Fold = dyn_cast<CXXFoldExpr>(IDC)) + CSE = cast<ConceptSpecializationExpr>(Fold->getLHS()); + else + CSE = cast<ConceptSpecializationExpr>(IDC); + ArrayRef<TemplateArgument> OldConverted = CSE->getTemplateArguments(); + SmallVector<TemplateArgument, 3> NewConverted; + NewConverted.reserve(OldConverted.size()); + if (OldConverted.front().getKind() == TemplateArgument::Pack) { + // The case: + // template<typename... T> concept C = true; + // template<C<int> T> struct S; -> constraint is C<{T, int}> + NewConverted.push_back(ConstrainedType); + for (auto &Arg : OldConverted.front().pack_elements().drop_front(1)) + NewConverted.push_back(Arg); + TemplateArgument NewPack(NewConverted); + + NewConverted.clear(); + NewConverted.push_back(NewPack); + assert(OldConverted.size() == 1 && + "Template parameter pack should be the last parameter"); + } else { + assert(OldConverted.front().getKind() == TemplateArgument::Type && + "Unexpected first argument kind for immediately-declared " + "constraint"); + NewConverted.push_back(ConstrainedType); + for (auto &Arg : OldConverted.drop_front(1)) + NewConverted.push_back(Arg); + } + Expr *NewIDC = ConceptSpecializationExpr::Create( + C, NestedNameSpecifierLoc(), /*TemplateKWLoc=*/SourceLocation(), + CSE->getConceptNameInfo(), /*FoundDecl=*/CSE->getNamedConcept(), + CSE->getNamedConcept(), + // Actually canonicalizing a TemplateArgumentLoc is difficult so we + // simply omit the ArgsAsWritten + /*ArgsAsWritten=*/nullptr, NewConverted, nullptr); + + if (auto *OrigFold = dyn_cast<CXXFoldExpr>(IDC)) + NewIDC = new (C) CXXFoldExpr(OrigFold->getType(), SourceLocation(), NewIDC, + BinaryOperatorKind::BO_LAnd, + SourceLocation(), /*RHS=*/nullptr, + SourceLocation(), /*NumExpansions=*/None); + return NewIDC; +} + TemplateTemplateParmDecl * ASTContext::getCanonicalTemplateTemplateParmDecl( TemplateTemplateParmDecl *TTP) const { @@ -743,68 +798,23 @@ ASTContext::getCanonicalTemplateTemplateParmDecl( TTP->isExpandedParameterPack() ? llvm::Optional<unsigned>(TTP->getNumExpansionParameters()) : None); if (const auto *TC = TTP->getTypeConstraint()) { - // This is a bit ugly - we need to form a new immediately-declared - // constraint that references the new parameter; this would ideally - // require semantic analysis (e.g. template<C T> struct S {}; - the - // converted arguments of C<T> could be an argument pack if C is - // declared as template<typename... T> concept C = ...). - // We don't have semantic analysis here so we dig deep into the - // ready-made constraint expr and change the thing manually. - Expr *IDC = TC->getImmediatelyDeclaredConstraint(); - ConceptSpecializationExpr *CSE; - if (const auto *Fold = dyn_cast<CXXFoldExpr>(IDC)) - CSE = cast<ConceptSpecializationExpr>(Fold->getLHS()); - else - CSE = cast<ConceptSpecializationExpr>(IDC); - ArrayRef<TemplateArgument> OldConverted = CSE->getTemplateArguments(); - SmallVector<TemplateArgument, 3> NewConverted; - NewConverted.reserve(OldConverted.size()); - QualType ParamAsArgument(NewTTP->getTypeForDecl(), 0); - if (OldConverted.front().getKind() == TemplateArgument::Pack) { - // The case: - // template<typename... T> concept C = true; - // template<C<int> T> struct S; -> constraint is C<{T, int}> - NewConverted.push_back(ParamAsArgument); - for (auto &Arg : OldConverted.front().pack_elements().drop_front(1)) - NewConverted.push_back(Arg); - TemplateArgument NewPack(NewConverted); - - NewConverted.clear(); - NewConverted.push_back(NewPack); - assert(OldConverted.size() == 1 && - "Template parameter pack should be the last parameter"); - } else { - assert(OldConverted.front().getKind() == TemplateArgument::Type && - "Unexpected first argument kind for immediately-declared " - "constraint"); - NewConverted.push_back(ParamAsArgument); - for (auto &Arg : OldConverted.drop_front(1)) - NewConverted.push_back(Arg); - } - Expr *NewIDC = ConceptSpecializationExpr::Create(*this, - NestedNameSpecifierLoc(), /*TemplateKWLoc=*/SourceLocation(), - CSE->getConceptNameInfo(), /*FoundDecl=*/CSE->getNamedConcept(), - CSE->getNamedConcept(), - // Actually canonicalizing a TemplateArgumentLoc is difficult so we - // simply omit the ArgsAsWritten - /*ArgsAsWritten=*/nullptr, NewConverted, nullptr); - - if (auto *OrigFold = dyn_cast<CXXFoldExpr>(IDC)) - NewIDC = new (*this) CXXFoldExpr(OrigFold->getType(), - SourceLocation(), NewIDC, - BinaryOperatorKind::BO_LAnd, - SourceLocation(), /*RHS=*/nullptr, - SourceLocation(), - /*NumExpansions=*/None); - + Expr *NewIDC = canonicalizeImmediatelyDeclaredConstraint( + *this, TC->getImmediatelyDeclaredConstraint(), + ParamAsArgument); + TemplateArgumentListInfo CanonArgsAsWritten; + if (auto *Args = TC->getTemplateArgsAsWritten()) + for (const auto &ArgLoc : Args->arguments()) + CanonArgsAsWritten.addArgument( + TemplateArgumentLoc(ArgLoc.getArgument(), + TemplateArgumentLocInfo())); NewTTP->setTypeConstraint( NestedNameSpecifierLoc(), DeclarationNameInfo(TC->getNamedConcept()->getDeclName(), SourceLocation()), /*FoundDecl=*/nullptr, // Actually canonicalizing a TemplateArgumentLoc is difficult so we // simply omit the ArgsAsWritten - CSE->getNamedConcept(), /*ArgsAsWritten=*/nullptr, NewIDC); + TC->getNamedConcept(), /*ArgsAsWritten=*/nullptr, NewIDC); } CanonParams.push_back(NewTTP); } else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(*P)) { @@ -839,6 +849,13 @@ ASTContext::getCanonicalTemplateTemplateParmDecl( NTTP->isParameterPack(), TInfo); } + if (AutoType *AT = T->getContainedAutoType()) { + if (AT->isConstrained()) { + Param->setPlaceholderTypeConstraint( + canonicalizeImmediatelyDeclaredConstraint( + *this, NTTP->getPlaceholderTypeConstraint(), T)); + } + } CanonParams.push_back(Param); } else @@ -943,7 +960,7 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, Builtin::Context &builtins) : ConstantArrayTypes(this_()), FunctionProtoTypes(this_()), TemplateSpecializationTypes(this_()), - DependentTemplateSpecializationTypes(this_()), + DependentTemplateSpecializationTypes(this_()), AutoTypes(this_()), SubstTemplateTemplateParmPacks(this_()), CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts), SanitizerBL(new SanitizerBlacklist(LangOpts.SanitizerBlacklistFiles, SM)), @@ -5124,21 +5141,29 @@ QualType ASTContext::getUnaryTransformType(QualType BaseType, /// getAutoType - Return the uniqued reference to the 'auto' type which has been /// deduced to the given type, or to the canonical undeduced 'auto' type, or the /// canonical deduced-but-dependent 'auto' type. -QualType ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, - bool IsDependent, bool IsPack) const { +QualType +ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, + bool IsDependent, bool IsPack, + ConceptDecl *TypeConstraintConcept, + ArrayRef<TemplateArgument> TypeConstraintArgs) const { assert((!IsPack || IsDependent) && "only use IsPack for a dependent pack"); - if (DeducedType.isNull() && Keyword == AutoTypeKeyword::Auto && !IsDependent) + if (DeducedType.isNull() && Keyword == AutoTypeKeyword::Auto && + !TypeConstraintConcept && !IsDependent) return getAutoDeductType(); // Look in the folding set for an existing type. void *InsertPos = nullptr; llvm::FoldingSetNodeID ID; - AutoType::Profile(ID, DeducedType, Keyword, IsDependent, IsPack); + AutoType::Profile(ID, *this, DeducedType, Keyword, IsDependent, + TypeConstraintConcept, TypeConstraintArgs); if (AutoType *AT = AutoTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(AT, 0); - auto *AT = new (*this, TypeAlignment) - AutoType(DeducedType, Keyword, IsDependent, IsPack); + void *Mem = Allocate(sizeof(AutoType) + + sizeof(TemplateArgument) * TypeConstraintArgs.size(), + TypeAlignment); + auto *AT = new (Mem) AutoType(DeducedType, Keyword, IsDependent, IsPack, + TypeConstraintConcept, TypeConstraintArgs); Types.push_back(AT); if (InsertPos) AutoTypes.InsertNode(AT, InsertPos); @@ -5200,7 +5225,8 @@ QualType ASTContext::getAutoDeductType() const { if (AutoDeductTy.isNull()) AutoDeductTy = QualType( new (*this, TypeAlignment) AutoType(QualType(), AutoTypeKeyword::Auto, - /*dependent*/false, /*pack*/false), + /*dependent*/false, /*pack*/false, + /*concept*/nullptr, /*args*/{}), 0); return AutoDeductTy; } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 22fb67478c969..1f2ce30398c91 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1366,9 +1366,21 @@ ExpectedType ASTNodeImporter::VisitAutoType(const AutoType *T) { if (!ToDeducedTypeOrErr) return ToDeducedTypeOrErr.takeError(); - return Importer.getToContext().getAutoType(*ToDeducedTypeOrErr, - T->getKeyword(), - /*IsDependent*/false); + ExpectedDecl ToTypeConstraintConcept = import(T->getTypeConstraintConcept()); + if (!ToTypeConstraintConcept) + return ToTypeConstraintConcept.takeError(); + + SmallVector<TemplateArgument, 2> ToTemplateArgs; + ArrayRef<TemplateArgument> FromTemplateArgs = T->getTypeConstraintArguments(); + if (Error Err = ImportTemplateArguments(FromTemplateArgs.data(), + FromTemplateArgs.size(), + ToTemplateArgs)) + return std::move(Err); + + return Importer.getToContext().getAutoType( + *ToDeducedTypeOrErr, T->getKeyword(), /*IsDependent*/false, + /*IsPack=*/false, cast_or_null<ConceptDecl>(*ToTypeConstraintConcept), + ToTemplateArgs); } ExpectedType ASTNodeImporter::VisitInjectedClassNameType( diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index db48405055cda..91a2f3a8391bb 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -729,11 +729,31 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return false; break; - case Type::Auto: - if (!IsStructurallyEquivalent(Context, cast<AutoType>(T1)->getDeducedType(), - cast<AutoType>(T2)->getDeducedType())) + case Type::Auto: { + auto *Auto1 = cast<AutoType>(T1); + auto *Auto2 = cast<AutoType>(T2); + if (!IsStructurallyEquivalent(Context, Auto1->getDeducedType(), + Auto2->getDeducedType())) return false; + if (Auto1->isConstrained() != Auto2->isConstrained()) + return false; + if (Auto1->isConstrained()) { + if (Auto1->getTypeConstraintConcept() != + Auto2->getTypeConstraintConcept()) + return false; + ArrayRef<TemplateArgument> Auto1Args = + Auto1->getTypeConstraintArguments(); + ArrayRef<TemplateArgument> Auto2Args = + Auto2->getTypeConstraintArguments(); + if (Auto1Args.size() != Auto2Args.size()) + return false; + for (unsigned I = 0, N = Auto1Args.size(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, Auto1Args[I], Auto2Args[I])) + return false; + } + } break; + } case Type::DeducedTemplateSpecialization: { const auto *DT1 = cast<DeducedTemplateSpecializationType>(T1); diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 6ee767ccecf7d..cb4d61cac2c71 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -804,6 +804,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case OMPCapturedExpr: case Empty: case LifetimeExtendedTemporary: + case RequiresExprBody: // Never looked up by name. return 0; } @@ -1177,6 +1178,7 @@ DeclContext *DeclContext::getPrimaryContext() { case Decl::Captured: case Decl::OMPDeclareReduction: case Decl::OMPDeclareMapper: + case Decl::RequiresExprBody: // There is only one DeclContext for these entities. return this; diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 2ead1e70ea0d6..48e310e858b26 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1968,6 +1968,16 @@ CXXDeductionGuideDecl *CXXDeductionGuideDecl::CreateDeserialized(ASTContext &C, QualType(), nullptr, SourceLocation()); } +RequiresExprBodyDecl *RequiresExprBodyDecl::Create( + ASTContext &C, DeclContext *DC, SourceLocation StartLoc) { + return new (C, DC) RequiresExprBodyDecl(C, DC, StartLoc); +} + +RequiresExprBodyDecl *RequiresExprBodyDecl::CreateDeserialized(ASTContext &C, + unsigned ID) { + return new (C, ID) RequiresExprBodyDecl(C, nullptr, SourceLocation()); +} + void CXXMethodDecl::anchor() {} bool CXXMethodDecl::isStatic() const { diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 95a2e26e0df84..b5e4ec2d7f43c 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -164,10 +164,15 @@ static void AdoptTemplateParameterList(TemplateParameterList *Params, void TemplateParameterList:: getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const { if (HasConstrainedParameters) - for (const NamedDecl *Param : *this) - if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) + for (const NamedDecl *Param : *this) { + if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) { if (const auto *TC = TTP->getTypeConstraint()) AC.push_back(TC->getImmediatelyDeclaredConstraint()); + } else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) { + if (const Expr *E = NTTP->getPlaceholderTypeConstraint()) + AC.push_back(E); + } + } if (HasRequiresClause) AC.push_back(getRequiresClause()); } @@ -483,7 +488,10 @@ static void ProfileTemplateParameterList(ASTContext &C, if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(D)) { ID.AddInteger(1); ID.AddBoolean(TTP->isParameterPack()); - // TODO: Concepts: profile type-constraints. + ID.AddBoolean(TTP->hasTypeConstraint()); + if (const TypeConstraint *TC = TTP->getTypeConstraint()) + TC->getImmediatelyDeclaredConstraint()->Profile(ID, C, + /*Canonical=*/true); continue; } const auto *TTP = cast<TemplateTemplateParmDecl>(D); @@ -684,8 +692,14 @@ NonTypeTemplateParmDecl::Create(const ASTContext &C, DeclContext *DC, unsigned D, unsigned P, IdentifierInfo *Id, QualType T, bool ParameterPack, TypeSourceInfo *TInfo) { - return new (C, DC) NonTypeTemplateParmDecl(DC, StartLoc, IdLoc, D, P, Id, - T, ParameterPack, TInfo); + AutoType *AT = + C.getLangOpts().CPlusPlus2a ? T->getContainedAutoType() : nullptr; + return new (C, DC, + additionalSizeToAlloc<std::pair<QualType, TypeSourceInfo *>, + Expr *>(0, + AT && AT->isConstrained() ? 1 : 0)) + NonTypeTemplateParmDecl(DC, StartLoc, IdLoc, D, P, Id, T, ParameterPack, + TInfo); } NonTypeTemplateParmDecl *NonTypeTemplateParmDecl::Create( @@ -693,26 +707,34 @@ NonTypeTemplateParmDecl *NonTypeTemplateParmDecl::Create( SourceLocation IdLoc, unsigned D, unsigned P, IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, ArrayRef<QualType> ExpandedTypes, ArrayRef<TypeSourceInfo *> ExpandedTInfos) { + AutoType *AT = TInfo->getType()->getContainedAutoType(); return new (C, DC, - additionalSizeToAlloc<std::pair<QualType, TypeSourceInfo *>>( - ExpandedTypes.size())) + additionalSizeToAlloc<std::pair<QualType, TypeSourceInfo *>, + Expr *>( + ExpandedTypes.size(), AT && AT->isConstrained() ? 1 : 0)) NonTypeTemplateParmDecl(DC, StartLoc, IdLoc, D, P, Id, T, TInfo, ExpandedTypes, ExpandedTInfos); } NonTypeTemplateParmDecl * -NonTypeTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID) { - return new (C, ID) NonTypeTemplateParmDecl(nullptr, SourceLocation(), - SourceLocation(), 0, 0, nullptr, - QualType(), false, nullptr); +NonTypeTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID, + bool HasTypeConstraint) { + return new (C, ID, additionalSizeToAlloc<std::pair<QualType, + TypeSourceInfo *>, + Expr *>(0, + HasTypeConstraint ? 1 : 0)) + NonTypeTemplateParmDecl(nullptr, SourceLocation(), SourceLocation(), + 0, 0, nullptr, QualType(), false, nullptr); } NonTypeTemplateParmDecl * NonTypeTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID, - unsigned NumExpandedTypes) { + unsigned NumExpandedTypes, + bool HasTypeConstraint) { auto *NTTP = - new (C, ID, additionalSizeToAlloc<std::pair<QualType, TypeSourceInfo *>>( - NumExpandedTypes)) + new (C, ID, additionalSizeToAlloc<std::pair<QualType, TypeSourceInfo *>, + Expr *>( + NumExpandedTypes, HasTypeConstraint ? 1 : 0)) NonTypeTemplateParmDecl(nullptr, SourceLocation(), SourceLocation(), 0, 0, nullptr, QualType(), nullptr, None, None); diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 73ddbc62482dd..8351989587662 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3457,6 +3457,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case OpaqueValueExprClass: case SourceLocExprClass: case ConceptSpecializationExprClass: + case RequiresExprClass: // These never have a side-effect. return false; diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 422227d787b10..e4bd218ae2d36 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclAccessPair.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/Expr.h" #include "clang/AST/LambdaCapture.h" @@ -1764,81 +1765,3 @@ CUDAKernelCallExpr *CUDAKernelCallExpr::CreateEmpty(const ASTContext &Ctx, alignof(CUDAKernelCallExpr)); return new (Mem) CUDAKernelCallExpr(NumArgs, Empty); } - -ConceptSpecializationExpr::ConceptSpecializationExpr(const ASTContext &C, - NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, - DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, - ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef<TemplateArgument> ConvertedArgs, - const ConstraintSatisfaction *Satisfaction) - : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary, - /*TypeDependent=*/false, - // All the flags below are set in setTemplateArguments. - /*ValueDependent=*/!Satisfaction, /*InstantiationDependent=*/false, - /*ContainsUnexpandedParameterPacks=*/false), - ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl, - NamedConcept, ArgsAsWritten), - NumTemplateArgs(ConvertedArgs.size()), - Satisfaction(Satisfaction ? - ASTConstraintSatisfaction::Create(C, *Satisfaction) : - nullptr) { - setTemplateArguments(ConvertedArgs); -} - -ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty, - unsigned NumTemplateArgs) - : Expr(ConceptSpecializationExprClass, Empty), ConceptReference(), - NumTemplateArgs(NumTemplateArgs) { } - -void ConceptSpecializationExpr::setTemplateArguments( - ArrayRef<TemplateArgument> Converted) { - assert(Converted.size() == NumTemplateArgs); - std::uninitialized_copy(Converted.begin(), Converted.end(), - getTrailingObjects<TemplateArgument>()); - bool IsInstantiationDependent = false; - bool ContainsUnexpandedParameterPack = false; - for (const TemplateArgument& Arg : Converted) { - if (Arg.isInstantiationDependent()) - IsInstantiationDependent = true; - if (Arg.containsUnexpandedParameterPack()) - ContainsUnexpandedParameterPack = true; - if (ContainsUnexpandedParameterPack && IsInstantiationDependent) - break; - } - - // Currently guaranteed by the fact concepts can only be at namespace-scope. - assert(!NestedNameSpec || - (!NestedNameSpec.getNestedNameSpecifier()->isInstantiationDependent() && - !NestedNameSpec.getNestedNameSpecifier() - ->containsUnexpandedParameterPack())); - setInstantiationDependent(IsInstantiationDependent); - setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); - assert((!isValueDependent() || isInstantiationDependent()) && - "should not be value-dependent"); -} - -ConceptSpecializationExpr * -ConceptSpecializationExpr::Create(const ASTContext &C, - NestedNameSpecifierLoc NNS, - SourceLocation TemplateKWLoc, - DeclarationNameInfo ConceptNameInfo, - NamedDecl *FoundDecl, - ConceptDecl *NamedConcept, - const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef<TemplateArgument> ConvertedArgs, - const ConstraintSatisfaction *Satisfaction) { - void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>( - ConvertedArgs.size())); - return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc, - ConceptNameInfo, FoundDecl, - NamedConcept, ArgsAsWritten, - ConvertedArgs, Satisfaction); -} - -ConceptSpecializationExpr * -ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty, - unsigned NumTemplateArgs) { - void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>( - NumTemplateArgs)); - return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs); -} diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 9dbf6fe9e0f06..d201af31f521e 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -193,6 +193,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::DesignatedInitUpdateExprClass: case Expr::SourceLocExprClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: return Cl::CL_PRValue; case Expr::ConstantExprClass: diff --git a/clang/lib/AST/ExprConcepts.cpp b/clang/lib/AST/ExprConcepts.cpp new file mode 100644 index 0000000000000..76d57ed5d5b1f --- /dev/null +++ b/clang/lib/AST/ExprConcepts.cpp @@ -0,0 +1,185 @@ +//===- ExprCXX.cpp - (C++) Expression AST Node Implementation -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the subclesses of Expr class declared in ExprCXX.h +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ExprConcepts.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTConcept.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/TemplateBase.h" +#include "clang/AST/Type.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/Support/TrailingObjects.h" +#include <algorithm> +#include <utility> +#include <string> + +using namespace clang; + +ConceptSpecializationExpr::ConceptSpecializationExpr(const ASTContext &C, + NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, + DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef<TemplateArgument> ConvertedArgs, + const ConstraintSatisfaction *Satisfaction) + : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary, + /*TypeDependent=*/false, + // All the flags below are set in setTemplateArguments. + /*ValueDependent=*/!Satisfaction, /*InstantiationDependent=*/false, + /*ContainsUnexpandedParameterPacks=*/false), + ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl, + NamedConcept, ArgsAsWritten), + NumTemplateArgs(ConvertedArgs.size()), + Satisfaction(Satisfaction ? + ASTConstraintSatisfaction::Create(C, *Satisfaction) : + nullptr) { + setTemplateArguments(ConvertedArgs); +} + +ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty, + unsigned NumTemplateArgs) + : Expr(ConceptSpecializationExprClass, Empty), ConceptReference(), + NumTemplateArgs(NumTemplateArgs) { } + +void ConceptSpecializationExpr::setTemplateArguments( + ArrayRef<TemplateArgument> Converted) { + assert(Converted.size() == NumTemplateArgs); + std::uninitialized_copy(Converted.begin(), Converted.end(), + getTrailingObjects<TemplateArgument>()); + bool IsInstantiationDependent = false; + bool ContainsUnexpandedParameterPack = false; + for (const TemplateArgument& Arg : Converted) { + if (Arg.isInstantiationDependent()) + IsInstantiationDependent = true; + if (Arg.containsUnexpandedParameterPack()) + ContainsUnexpandedParameterPack = true; + if (ContainsUnexpandedParameterPack && IsInstantiationDependent) + break; + } + + // Currently guaranteed by the fact concepts can only be at namespace-scope. + assert(!NestedNameSpec || + (!NestedNameSpec.getNestedNameSpecifier()->isInstantiationDependent() && + !NestedNameSpec.getNestedNameSpecifier() + ->containsUnexpandedParameterPack())); + setInstantiationDependent(IsInstantiationDependent); + setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); + assert((!isValueDependent() || isInstantiationDependent()) && + "should not be value-dependent"); +} + +ConceptSpecializationExpr * +ConceptSpecializationExpr::Create(const ASTContext &C, + NestedNameSpecifierLoc NNS, + SourceLocation TemplateKWLoc, + DeclarationNameInfo ConceptNameInfo, + NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef<TemplateArgument> ConvertedArgs, + const ConstraintSatisfaction *Satisfaction) { + void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>( + ConvertedArgs.size())); + return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc, + ConceptNameInfo, FoundDecl, + NamedConcept, ArgsAsWritten, + ConvertedArgs, Satisfaction); +} + +ConceptSpecializationExpr * +ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty, + unsigned NumTemplateArgs) { + void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>( + NumTemplateArgs)); + return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs); +} + +const TypeConstraint * +concepts::ExprRequirement::ReturnTypeRequirement::getTypeConstraint() const { + assert(isTypeConstraint()); + auto TPL = + TypeConstraintInfo.getPointer().get<TemplateParameterList *>(); + return cast<TemplateTypeParmDecl>(TPL->getParam(0)) + ->getTypeConstraint(); +} + +RequiresExpr::RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef<ParmVarDecl *> LocalParameters, + ArrayRef<concepts::Requirement *> Requirements, + SourceLocation RBraceLoc) + : Expr(RequiresExprClass, C.BoolTy, VK_RValue, OK_Ordinary, + /*TD=*/false, /*VD=*/false, /*ID=*/false, + /*ContainsUnexpandedParameterPack=*/false), + NumLocalParameters(LocalParameters.size()), + NumRequirements(Requirements.size()), Body(Body), RBraceLoc(RBraceLoc) { + RequiresExprBits.IsSatisfied = false; + RequiresExprBits.RequiresKWLoc = RequiresKWLoc; + bool Dependent = false; + bool ContainsUnexpandedParameterPack = false; + for (ParmVarDecl *P : LocalParameters) { + Dependent |= P->getType()->isInstantiationDependentType(); + ContainsUnexpandedParameterPack |= + P->getType()->containsUnexpandedParameterPack(); + } + RequiresExprBits.IsSatisfied = true; + for (concepts::Requirement *R : Requirements) { + Dependent |= R->isDependent(); + ContainsUnexpandedParameterPack |= R->containsUnexpandedParameterPack(); + if (!Dependent) { + RequiresExprBits.IsSatisfied = R->isSatisfied(); + if (!RequiresExprBits.IsSatisfied) + break; + } + } + std::copy(LocalParameters.begin(), LocalParameters.end(), + getTrailingObjects<ParmVarDecl *>()); + std::copy(Requirements.begin(), Requirements.end(), + getTrailingObjects<concepts::Requirement *>()); + RequiresExprBits.IsSatisfied |= Dependent; + setValueDependent(Dependent); + setInstantiationDependent(Dependent); + setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); +} + +RequiresExpr::RequiresExpr(ASTContext &C, EmptyShell Empty, + unsigned NumLocalParameters, + unsigned NumRequirements) + : Expr(RequiresExprClass, Empty), NumLocalParameters(NumLocalParameters), + NumRequirements(NumRequirements) { } + +RequiresExpr * +RequiresExpr::Create(ASTContext &C, SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef<ParmVarDecl *> LocalParameters, + ArrayRef<concepts::Requirement *> Requirements, + SourceLocation RBraceLoc) { + void *Mem = + C.Allocate(totalSizeToAlloc<ParmVarDecl *, concepts::Requirement *>( + LocalParameters.size(), Requirements.size()), + alignof(RequiresExpr)); + return new (Mem) RequiresExpr(C, RequiresKWLoc, Body, LocalParameters, + Requirements, RBraceLoc); +} + +RequiresExpr * +RequiresExpr::Create(ASTContext &C, EmptyShell Empty, + unsigned NumLocalParameters, unsigned NumRequirements) { + void *Mem = + C.Allocate(totalSizeToAlloc<ParmVarDecl *, concepts::Requirement *>( + NumLocalParameters, NumRequirements), + alignof(RequiresExpr)); + return new (Mem) RequiresExpr(C, Empty, NumLocalParameters, NumRequirements); +} diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index c4b27b5d1daa2..c79973507323d 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -9912,6 +9912,7 @@ public: bool VisitSizeOfPackExpr(const SizeOfPackExpr *E); bool VisitSourceLocExpr(const SourceLocExpr *E); bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E); + bool VisitRequiresExpr(const RequiresExpr *E); // FIXME: Missing: array subscript of vector, member of vector }; @@ -12524,6 +12525,9 @@ bool IntExprEvaluator::VisitConceptSpecializationExpr( return Success(E->isSatisfied(), E); } +bool IntExprEvaluator::VisitRequiresExpr(const RequiresExpr *E) { + return Success(E->isSatisfied(), E); +} bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { switch (E->getOpcode()) { @@ -14182,6 +14186,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::CXXScalarValueInitExprClass: case Expr::TypeTraitExprClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::CXXNoexceptExprClass: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 0d567edac5216..5d485e000750b 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -22,6 +22,7 @@ #include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/TypeLoc.h" @@ -3668,6 +3669,7 @@ recurse: case Expr::ConvertVectorExprClass: case Expr::StmtExprClass: case Expr::TypeTraitExprClass: + case Expr::RequiresExprClass: case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::VAArgExprClass: diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 27fdca1c4b9cf..1f9ff9e407dc2 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -857,6 +857,13 @@ public: void VisitAutoType(const AutoType *T) { ID.AddInteger((unsigned)T->getKeyword()); + ID.AddInteger(T->isConstrained()); + if (T->isConstrained()) { + AddDecl(T->getTypeConstraintConcept()); + ID.AddInteger(T->getNumArgs()); + for (const auto &TA : T->getTypeConstraintArguments()) + Hash.AddTemplateArgument(TA); + } VisitDeducedType(T); } diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index b6e4d8aff21e9..7409ae7ddc9e7 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -16,6 +16,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclGroup.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index c14bb886bb11a..45fd8ceae8d36 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2269,6 +2269,60 @@ void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) { Policy); } +void StmtPrinter::VisitRequiresExpr(RequiresExpr *E) { + OS << "requires "; + auto LocalParameters = E->getLocalParameters(); + if (!LocalParameters.empty()) { + OS << "("; + for (ParmVarDecl *LocalParam : LocalParameters) { + PrintRawDecl(LocalParam); + if (LocalParam != LocalParameters.back()) + OS << ", "; + } + + OS << ") "; + } + OS << "{ "; + auto Requirements = E->getRequirements(); + for (concepts::Requirement *Req : Requirements) { + if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) { + if (TypeReq->isSubstitutionFailure()) + OS << "<<error-type>>"; + else + TypeReq->getType()->getType().print(OS, Policy); + } else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) { + if (ExprReq->isCompound()) + OS << "{ "; + if (ExprReq->isExprSubstitutionFailure()) + OS << "<<error-expression>>"; + else + PrintExpr(ExprReq->getExpr()); + if (ExprReq->isCompound()) { + OS << " }"; + if (ExprReq->getNoexceptLoc().isValid()) + OS << " noexcept"; + const auto &RetReq = ExprReq->getReturnTypeRequirement(); + if (!RetReq.isEmpty()) { + OS << " -> "; + if (RetReq.isSubstitutionFailure()) + OS << "<<error-type>>"; + else if (RetReq.isTypeConstraint()) + RetReq.getTypeConstraint()->print(OS, Policy); + } + } + } else { + auto *NestedReq = cast<concepts::NestedRequirement>(Req); + OS << "requires "; + if (NestedReq->isSubstitutionFailure()) + OS << "<<error-expression>>"; + else + PrintExpr(NestedReq->getConstraintExpr()); + } + OS << "; "; + } + OS << "}"; +} + // C++ Coroutines TS void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 2aa5106e90fae..382ea5c8d7ef0 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1335,9 +1335,52 @@ void StmtProfiler::VisitAtomicExpr(const AtomicExpr *S) { void StmtProfiler::VisitConceptSpecializationExpr( const ConceptSpecializationExpr *S) { VisitExpr(S); - VisitDecl(S->getFoundDecl()); - VisitTemplateArguments(S->getTemplateArgsAsWritten()->getTemplateArgs(), - S->getTemplateArgsAsWritten()->NumTemplateArgs); + VisitDecl(S->getNamedConcept()); + for (const TemplateArgument &Arg : S->getTemplateArguments()) + VisitTemplateArgument(Arg); +} + +void StmtProfiler::VisitRequiresExpr(const RequiresExpr *S) { + VisitExpr(S); + ID.AddInteger(S->getLocalParameters().size()); + for (ParmVarDecl *LocalParam : S->getLocalParameters()) + VisitDecl(LocalParam); + ID.AddInteger(S->getRequirements().size()); + for (concepts::Requirement *Req : S->getRequirements()) { + if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) { + ID.AddInteger(concepts::Requirement::RK_Type); + ID.AddBoolean(TypeReq->isSubstitutionFailure()); + if (!TypeReq->isSubstitutionFailure()) + VisitType(TypeReq->getType()->getType()); + } else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) { + ID.AddInteger(concepts::Requirement::RK_Compound); + ID.AddBoolean(ExprReq->isExprSubstitutionFailure()); + if (!ExprReq->isExprSubstitutionFailure()) + Visit(ExprReq->getExpr()); + // C++2a [expr.prim.req.compound]p1 Example: + // [...] The compound-requirement in C1 requires that x++ is a valid + // expression. It is equivalent to the simple-requirement x++; [...] + // We therefore do not profile isSimple() here. + ID.AddBoolean(ExprReq->getNoexceptLoc().isValid()); + const concepts::ExprRequirement::ReturnTypeRequirement &RetReq = + ExprReq->getReturnTypeRequirement(); + if (RetReq.isEmpty()) { + ID.AddInteger(0); + } else if (RetReq.isTypeConstraint()) { + ID.AddInteger(1); + Visit(RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint()); + } else { + assert(RetReq.isSubstitutionFailure()); + ID.AddInteger(2); + } + } else { + ID.AddInteger(concepts::Requirement::RK_Nested); + auto *NestedReq = cast<concepts::NestedRequirement>(Req); + ID.AddBoolean(NestedReq->isSubstitutionFailure()); + if (!NestedReq->isSubstitutionFailure()) + Visit(NestedReq->getConstraintExpr()); + } + } } static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S, diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp index db16c2a06b64f..6f0ebf232e77c 100644 --- a/clang/lib/AST/TemplateBase.cpp +++ b/clang/lib/AST/TemplateBase.cpp @@ -561,7 +561,7 @@ const DiagnosticBuilder &clang::operator<<(const DiagnosticBuilder &DB, } const ASTTemplateArgumentListInfo * -ASTTemplateArgumentListInfo::Create(ASTContext &C, +ASTTemplateArgumentListInfo::Create(const ASTContext &C, const TemplateArgumentListInfo &List) { std::size_t size = totalSizeToAlloc<TemplateArgumentLoc>(List.size()); void *Mem = C.Allocate(size, alignof(ASTTemplateArgumentListInfo)); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 965ad17fcfa5b..c9b571862c199 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1201,6 +1201,11 @@ void TextNodeDumper::VisitAutoType(const AutoType *T) { OS << " decltype(auto)"; if (!T->isDeduced()) OS << " undeduced"; + if (T->isConstrained()) { + dumpDeclRef(T->getTypeConstraintConcept()); + for (const auto &Arg : T->getTypeConstraintArguments()) + VisitTemplateArgument(Arg); + } } void TextNodeDumper::VisitTemplateSpecializationType( diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index c5ad711d872e0..5099494da5fd6 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1114,7 +1114,9 @@ public: return QualType(T, 0); return Ctx.getAutoType(deducedType, T->getKeyword(), - T->isDependentType()); + T->isDependentType(), /*IsPack=*/false, + T->getTypeConstraintConcept(), + T->getTypeConstraintArguments()); } // FIXME: Non-trivial to implement, but important for C++ @@ -4158,3 +4160,35 @@ void clang::FixedPointValueToString(SmallVectorImpl<char> &Str, /*HasUnsignedPadding=*/false); APFixedPoint(Val, FXSema).toString(Str); } + +AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword, + bool IsDeducedAsDependent, bool IsDeducedAsPack, + ConceptDecl *TypeConstraintConcept, + ArrayRef<TemplateArgument> TypeConstraintArgs) + : DeducedType(Auto, DeducedAsType, IsDeducedAsDependent, + IsDeducedAsDependent, IsDeducedAsPack) { + AutoTypeBits.Keyword = (unsigned)Keyword; + AutoTypeBits.NumArgs = TypeConstraintArgs.size(); + this->TypeConstraintConcept = TypeConstraintConcept; + if (TypeConstraintConcept) { + TemplateArgument *ArgBuffer = getArgBuffer(); + for (const TemplateArgument &Arg : TypeConstraintArgs) { + if (Arg.containsUnexpandedParameterPack()) + setContainsUnexpandedParameterPack(); + + new (ArgBuffer++) TemplateArgument(Arg); + } + } +} + +void AutoType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, + QualType Deduced, AutoTypeKeyword Keyword, + bool IsDependent, ConceptDecl *CD, + ArrayRef<TemplateArgument> Arguments) { + ID.AddPointer(Deduced.getAsOpaquePtr()); + ID.AddInteger((unsigned)Keyword); + ID.AddBoolean(IsDependent); + ID.AddPointer(CD); + for (const TemplateArgument &Arg : Arguments) + Arg.Profile(ID, Context); +} diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index 6e67ca8e0af72..665a86f2c1432 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/TypeLoc.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/Expr.h" @@ -589,3 +590,97 @@ void TemplateSpecializationTypeLoc::initializeArgLocs(ASTContext &Context, } } } + +DeclarationNameInfo AutoTypeLoc::getConceptNameInfo() const { + return DeclarationNameInfo(getNamedConcept()->getDeclName(), + getLocalData()->ConceptNameLoc); +} + +void AutoTypeLoc::initializeLocal(ASTContext &Context, SourceLocation Loc) { + setNestedNameSpecifierLoc(NestedNameSpecifierLoc()); + setTemplateKWLoc(Loc); + setConceptNameLoc(Loc); + setFoundDecl(nullptr); + setRAngleLoc(Loc); + setLAngleLoc(Loc); + TemplateSpecializationTypeLoc::initializeArgLocs(Context, getNumArgs(), + getTypePtr()->getArgs(), + getArgInfos(), Loc); + setNameLoc(Loc); +} + + +namespace { + + class GetContainedAutoTypeLocVisitor : + public TypeLocVisitor<GetContainedAutoTypeLocVisitor, TypeLoc> { + public: + using TypeLocVisitor<GetContainedAutoTypeLocVisitor, TypeLoc>::Visit; + + TypeLoc VisitAutoTypeLoc(AutoTypeLoc TL) { + return TL; + } + + // Only these types can contain the desired 'auto' type. + + TypeLoc VisitElaboratedTypeLoc(ElaboratedTypeLoc T) { + return Visit(T.getNamedTypeLoc()); + } + + TypeLoc VisitQualifiedTypeLoc(QualifiedTypeLoc T) { + return Visit(T.getUnqualifiedLoc()); + } + + TypeLoc VisitPointerTypeLoc(PointerTypeLoc T) { + return Visit(T.getPointeeLoc()); + } + + TypeLoc VisitBlockPointerTypeLoc(BlockPointerTypeLoc T) { + return Visit(T.getPointeeLoc()); + } + + TypeLoc VisitReferenceTypeLoc(ReferenceTypeLoc T) { + return Visit(T.getPointeeLoc()); + } + + TypeLoc VisitMemberPointerTypeLoc(MemberPointerTypeLoc T) { + return Visit(T.getPointeeLoc()); + } + + TypeLoc VisitArrayTypeLoc(ArrayTypeLoc T) { + return Visit(T.getElementLoc()); + } + + TypeLoc VisitFunctionTypeLoc(FunctionTypeLoc T) { + return Visit(T.getReturnLoc()); + } + + TypeLoc VisitParenTypeLoc(ParenTypeLoc T) { + return Visit(T.getInnerLoc()); + } + + TypeLoc VisitAttributedTypeLoc(AttributedTypeLoc T) { + return Visit(T.getModifiedLoc()); + } + + TypeLoc VisitMacroQualifiedTypeLoc(MacroQualifiedTypeLoc T) { + return Visit(T.getInnerLoc()); + } + + TypeLoc VisitAdjustedTypeLoc(AdjustedTypeLoc T) { + return Visit(T.getOriginalLoc()); + } + + TypeLoc VisitPackExpansionTypeLoc(PackExpansionTypeLoc T) { + return Visit(T.getPatternLoc()); + } + }; + +} // namespace + +AutoTypeLoc TypeLoc::getContainedAutoTypeLoc() const { + TypeLoc Res = GetContainedAutoTypeLocVisitor().Visit(*this); + if (Res.isNull()) + return AutoTypeLoc(); + return Res.getAs<AutoTypeLoc>(); +} diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index c2f4baec989ee..4a7e765a2bd82 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1046,6 +1046,13 @@ void TypePrinter::printAutoBefore(const AutoType *T, raw_ostream &OS) { if (!T->getDeducedType().isNull()) { printBefore(T->getDeducedType(), OS); } else { + if (T->isConstrained()) { + OS << T->getTypeConstraintConcept()->getName(); + auto Args = T->getTypeConstraintArguments(); + if (!Args.empty()) + printTemplateArgumentList(OS, Args, Policy); + OS << ' '; + } switch (T->getKeyword()) { case AutoTypeKeyword::Auto: OS << "auto"; break; case AutoTypeKeyword::DecltypeAuto: OS << "decltype(auto)"; break; @@ -1234,20 +1241,18 @@ void TypePrinter::printEnumAfter(const EnumType *T, raw_ostream &OS) {} void TypePrinter::printTemplateTypeParmBefore(const TemplateTypeParmType *T, raw_ostream &OS) { - if (IdentifierInfo *Id = T->getIdentifier()) - OS << Id->getName(); - else { - bool IsLambdaAutoParam = false; - if (auto D = T->getDecl()) { - if (auto M = dyn_cast_or_null<CXXMethodDecl>(D->getDeclContext())) - IsLambdaAutoParam = D->isImplicit() && M->getParent()->isLambda(); + TemplateTypeParmDecl *D = T->getDecl(); + if (D && D->isImplicit()) { + if (auto *TC = D->getTypeConstraint()) { + TC->print(OS, Policy); + OS << ' '; } + OS << "auto"; + } else if (IdentifierInfo *Id = T->getIdentifier()) + OS << Id->getName(); + else + OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex(); - if (IsLambdaAutoParam) - OS << "auto"; - else - OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex(); - } spaceBeforePlaceHolder(OS); } diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp index 4aebea19924f7..ee25bd883caf6 100644 --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -142,7 +142,7 @@ static KeywordStatus getKeywordStatus(const LangOptions &LangOpts, // We treat bridge casts as objective-C keywords so we can warn on them // in non-arc mode. if (LangOpts.ObjC && (Flags & KEYOBJC)) return KS_Enabled; - if (LangOpts.ConceptsTS && (Flags & KEYCONCEPTS)) return KS_Enabled; + if (LangOpts.CPlusPlus2a && (Flags & KEYCONCEPTS)) return KS_Enabled; if (LangOpts.Coroutines && (Flags & KEYCOROUTINES)) return KS_Enabled; if (LangOpts.ModulesTS && (Flags & KEYMODULES)) return KS_Enabled; if (LangOpts.CPlusPlus && (Flags & KEYALLCXX)) return KS_Future; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 09fd3087b494a..2d20f92fbb3d2 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -3222,6 +3222,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, Builder.CreateZExt(EmitSignBit(*this, EmitScalarExpr(E->getArg(0))), ConvertType(E->getType()))); } + case Builtin::BI__warn_memset_zero_len: + return RValue::getIgnored(); case Builtin::BI__annotation: { // Re-encode each wide string to UTF8 and make an MDString. SmallVector<Metadata *, 1> Strings; diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 5aac7a8d54c77..60f1dba7c768a 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -111,6 +111,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::Empty: case Decl::Concept: case Decl::LifetimeExtendedTemporary: + case Decl::RequiresExprBody: // None of these decls require codegen support. return; diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 3f23fe11e4f58..de5c3a03fb68d 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -680,6 +680,10 @@ public: return Builder.getInt1(E->isSatisfied()); } + Value *VisitRequiresExpr(const RequiresExpr *E) { + return Builder.getInt1(E->isSatisfied()); + } + Value *VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) { return llvm::ConstantInt::get(Builder.getInt32Ty(), E->getValue()); } diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 2bf94f697e01c..648e6d9c214a8 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -820,13 +820,18 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, llvm::itostr(CGM.getCodeGenOpts().XRayInstructionThreshold)); } + unsigned Count, Offset; if (const auto *Attr = D->getAttr<PatchableFunctionEntryAttr>()) { - // Attr->getStart is currently ignored. - Fn->addFnAttr("patchable-function-entry", - std::to_string(Attr->getCount())); - } else if (unsigned Count = CGM.getCodeGenOpts().PatchableFunctionEntryCount) { - Fn->addFnAttr("patchable-function-entry", - std::to_string(Count)); + Count = Attr->getCount(); + Offset = Attr->getOffset(); + } else { + Count = CGM.getCodeGenOpts().PatchableFunctionEntryCount; + Offset = CGM.getCodeGenOpts().PatchableFunctionEntryOffset; + } + if (Count && Offset <= Count) { + Fn->addFnAttr("patchable-function-entry", std::to_string(Count - Offset)); + if (Offset) + Fn->addFnAttr("patchable-function-prefix", std::to_string(Offset)); } } diff --git a/clang/lib/Driver/Compilation.cpp b/clang/lib/Driver/Compilation.cpp index ba188f5c4083c..25aec3690f210 100644 --- a/clang/lib/Driver/Compilation.cpp +++ b/clang/lib/Driver/Compilation.cpp @@ -172,7 +172,7 @@ int Compilation::ExecuteCommand(const Command &C, } if (getDriver().CCPrintOptions) - *OS << "[Logging clang options]"; + *OS << "[Logging clang options]\n"; C.Print(*OS, "\n", /*Quote=*/getDriver().CCPrintOptions); } diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index d57c3a1cdbb89..7dab2a022d929 100644 --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -373,7 +373,7 @@ int Command::Execute(ArrayRef<llvm::Optional<StringRef>> Redirects, void CC1Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo) const { - OS << " (in-process)"; + OS << " (in-process)\n"; Command::Print(OS, Terminator, Quote, CrashInfo); } diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 3ebbd30195b31..cab97b1a601a3 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -68,8 +68,7 @@ static ToolChain::RTTIMode CalculateRTTIMode(const ArgList &Args, } // -frtti is default, except for the PS4 CPU. - return (Triple.isPS4CPU() || Triple.isNVPTX()) ? ToolChain::RM_Disabled - : ToolChain::RM_Enabled; + return (Triple.isPS4CPU()) ? ToolChain::RM_Disabled : ToolChain::RM_Enabled; } ToolChain::ToolChain(const Driver &D, const llvm::Triple &T, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 9b3055413e9e6..647465863d3e3 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5077,20 +5077,23 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (Arg *A = Args.getLastArg(options::OPT_fpatchable_function_entry_EQ)) { StringRef S0 = A->getValue(), S = S0; - unsigned Size, Start = 0; + unsigned Size, Offset = 0; if (!Triple.isAArch64() && Triple.getArch() != llvm::Triple::x86 && Triple.getArch() != llvm::Triple::x86_64) D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getAsString(Args) << TripleStr; else if (S.consumeInteger(10, Size) || (!S.empty() && (!S.consume_front(",") || - S.consumeInteger(10, Start) || !S.empty()))) + S.consumeInteger(10, Offset) || !S.empty()))) D.Diag(diag::err_drv_invalid_argument_to_option) << S0 << A->getOption().getName(); - else if (Start) + else if (Size < Offset) D.Diag(diag::err_drv_unsupported_fpatchable_function_entry_argument); - else + else { CmdArgs.push_back(Args.MakeArgString(A->getSpelling() + Twine(Size))); + CmdArgs.push_back(Args.MakeArgString( + "-fpatchable-function-entry-offset=" + Twine(Offset))); + } } if (TC.SupportsProfiling()) { diff --git a/clang/lib/Driver/ToolChains/HIP.cpp b/clang/lib/Driver/ToolChains/HIP.cpp index f89e648948aba..da7004cf283f2 100644 --- a/clang/lib/Driver/ToolChains/HIP.cpp +++ b/clang/lib/Driver/ToolChains/HIP.cpp @@ -105,9 +105,8 @@ const char *AMDGCN::Linker::constructLLVMLinkCommand( CmdArgs.push_back("-o"); auto OutputFileName = getOutputFileName(C, OutputFilePrefix, "-linked", "bc"); CmdArgs.push_back(OutputFileName); - SmallString<128> ExecPath(C.getDriver().Dir); - llvm::sys::path::append(ExecPath, "llvm-link"); - const char *Exec = Args.MakeArgString(ExecPath); + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath("llvm-link")); C.addCommand(std::make_unique<Command>(JA, *this, Exec, CmdArgs, Inputs)); return OutputFileName; } @@ -133,9 +132,8 @@ const char *AMDGCN::Linker::constructOptCommand( auto OutputFileName = getOutputFileName(C, OutputFilePrefix, "-optimized", "bc"); OptArgs.push_back(OutputFileName); - SmallString<128> OptPath(C.getDriver().Dir); - llvm::sys::path::append(OptPath, "opt"); - const char *OptExec = Args.MakeArgString(OptPath); + const char *OptExec = + Args.MakeArgString(getToolChain().GetProgramPath("opt")); C.addCommand(std::make_unique<Command>(JA, *this, OptExec, OptArgs, Inputs)); return OutputFileName; } @@ -180,9 +178,7 @@ const char *AMDGCN::Linker::constructLlcCommand( auto LlcOutputFile = getOutputFileName(C, OutputFilePrefix, "", OutputIsAsm ? "s" : "o"); LlcArgs.push_back(LlcOutputFile); - SmallString<128> LlcPath(C.getDriver().Dir); - llvm::sys::path::append(LlcPath, "llc"); - const char *Llc = Args.MakeArgString(LlcPath); + const char *Llc = Args.MakeArgString(getToolChain().GetProgramPath("llc")); C.addCommand(std::make_unique<Command>(JA, *this, Llc, LlcArgs, Inputs)); return LlcOutputFile; } @@ -196,9 +192,7 @@ void AMDGCN::Linker::constructLldCommand(Compilation &C, const JobAction &JA, // The output from ld.lld is an HSA code object file. ArgStringList LldArgs{ "-flavor", "gnu", "-shared", "-o", Output.getFilename(), InputFileName}; - SmallString<128> LldPath(C.getDriver().Dir); - llvm::sys::path::append(LldPath, "lld"); - const char *Lld = Args.MakeArgString(LldPath); + const char *Lld = Args.MakeArgString(getToolChain().GetProgramPath("lld")); C.addCommand(std::make_unique<Command>(JA, *this, Lld, LldArgs, Inputs)); } @@ -230,9 +224,8 @@ void AMDGCN::constructHIPFatbinCommand(Compilation &C, const JobAction &JA, Args.MakeArgString(std::string("-outputs=").append(OutputFileName)); BundlerArgs.push_back(BundlerOutputArg); - SmallString<128> BundlerPath(C.getDriver().Dir); - llvm::sys::path::append(BundlerPath, "clang-offload-bundler"); - const char *Bundler = Args.MakeArgString(BundlerPath); + const char *Bundler = Args.MakeArgString( + T.getToolChain().GetProgramPath("clang-offload-bundler")); C.addCommand(std::make_unique<Command>(JA, T, Bundler, BundlerArgs, Inputs)); } diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index d5d394e61926a..88564e02f23ed 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -2596,7 +2596,7 @@ bool TokenAnnotator::spaceRequiredBeforeParens(const FormatToken &Right) const { /// otherwise. static bool isKeywordWithCondition(const FormatToken &Tok) { return Tok.isOneOf(tok::kw_if, tok::kw_for, tok::kw_while, tok::kw_switch, - tok::kw_constexpr); + tok::kw_constexpr, tok::kw_catch); } bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index e1e59565083b7..4e5babdbaa038 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1103,6 +1103,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.PatchableFunctionEntryCount = getLastArgIntValue(Args, OPT_fpatchable_function_entry_EQ, 0, Diags); + Opts.PatchableFunctionEntryOffset = getLastArgIntValue( + Args, OPT_fpatchable_function_entry_offset_EQ, 0, Diags); Opts.InstrumentForProfiling = Args.hasArg(OPT_pg); Opts.CallFEntry = Args.hasArg(OPT_mfentry); Opts.MNopMCount = Args.hasArg(OPT_mnop_mcount); @@ -2852,7 +2854,10 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, << A->getValue(); Opts.NewAlignOverride = 0; } - Opts.ConceptsTS = Args.hasArg(OPT_fconcepts_ts); + Opts.ConceptSatisfactionCaching = + !Args.hasArg(OPT_fno_concept_satisfaction_caching); + if (Args.hasArg(OPT_fconcepts_ts)) + Diags.Report(diag::warn_fe_concepts_ts_flag); Opts.HeinousExtensions = Args.hasArg(OPT_fheinous_gnu_extensions); Opts.AccessControl = !Args.hasArg(OPT_fno_access_control); Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors); diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 8574d0a7e8132..935c64a0fa13f 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -429,6 +429,10 @@ private: return "ConstraintNormalization"; case CodeSynthesisContext::ParameterMappingSubstitution: return "ParameterMappingSubstitution"; + case CodeSynthesisContext::RequirementInstantiation: + return "RequirementInstantiation"; + case CodeSynthesisContext::NestedRequirementConstraintsCheck: + return "NestedRequirementConstraintsCheck"; } return ""; } diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 2c7e3a56c0436..8a0ff55e44fc6 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -548,7 +548,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, // C++20 features. if (LangOpts.CPlusPlus2a) { //Builder.defineMacro("__cpp_aggregate_paren_init", "201902L"); - //Builder.defineMacro("__cpp_concepts", "201907L"); + Builder.defineMacro("__cpp_concepts", "201907L"); Builder.defineMacro("__cpp_conditional_explicit", "201806L"); //Builder.defineMacro("__cpp_consteval", "201811L"); Builder.defineMacro("__cpp_constexpr_dynamic_alloc", "201907L"); @@ -564,8 +564,6 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, Builder.defineMacro("__cpp_impl_destroying_delete", "201806L"); // TS features. - if (LangOpts.ConceptsTS) - Builder.defineMacro("__cpp_experimental_concepts", "1L"); if (LangOpts.Coroutines) Builder.defineMacro("__cpp_coroutines", "201703L"); } diff --git a/clang/lib/Headers/ppc_wrappers/emmintrin.h b/clang/lib/Headers/ppc_wrappers/emmintrin.h index 293276cc9be05..4dcb8485e2e9b 100644 --- a/clang/lib/Headers/ppc_wrappers/emmintrin.h +++ b/clang/lib/Headers/ppc_wrappers/emmintrin.h @@ -1749,7 +1749,7 @@ _mm_sll_epi64 (__m128i __A, __m128i __B) lshift = vec_splat ((__v2du) __B, 0); shmask = vec_cmplt (lshift, shmax); result = vec_sl ((__v2du) __A, lshift); - result = vec_sel ((__v2du) shmask, result, shmask); + result = (__v2du)vec_sel ((__v2df) shmask, (__v2df)result, shmask); return (__m128i) result; } @@ -1843,7 +1843,7 @@ _mm_srl_epi64 (__m128i __A, __m128i __B) rshift = vec_splat ((__v2du) __B, 0); shmask = vec_cmplt (rshift, shmax); result = vec_sr ((__v2du) __A, rshift); - result = vec_sel ((__v2du) shmask, result, shmask); + result = (__v2du)vec_sel ((__v2df) shmask, (__v2df)result, shmask); return (__m128i) result; } diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index f8b5fec438007..a75965784168c 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -133,7 +133,9 @@ NamedDecl *Parser::ParseCXXInlineMethodDef( LexedMethod* LM = new LexedMethod(this, FnD); getCurrentClass().LateParsedDeclarations.push_back(LM); - LM->TemplateScope = getCurScope()->isTemplateParamScope(); + LM->TemplateScope = getCurScope()->isTemplateParamScope() || + (FnD && isa<FunctionTemplateDecl>(FnD) && + cast<FunctionTemplateDecl>(FnD)->isAbbreviated()); CachedTokens &Toks = LM->Toks; tok::TokenKind kind = Tok.getKind(); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 69a3ed9cbad77..4af993c4527ff 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2962,6 +2962,7 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS, case Sema::NC_ContextIndependentExpr: case Sema::NC_VarTemplate: case Sema::NC_FunctionTemplate: + case Sema::NC_Concept: // Might be a redeclaration of a prior entity. break; } @@ -3177,7 +3178,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, DSContext == DeclSpecContext::DSC_class) && TemplateId->Name && Actions.isCurrentClassName(*TemplateId->Name, getCurScope(), &SS) && - isConstructorDeclarator(/*Unqualified*/ false)) { + isConstructorDeclarator(/*Unqualified=*/false)) { // The user meant this to be an out-of-line constructor // definition, but template arguments are not allowed // there. Just allow this as a constructor; we'll @@ -3189,7 +3190,19 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, ConsumeAnnotationToken(); // The C++ scope. assert(Tok.is(tok::annot_template_id) && "ParseOptionalCXXScopeSpecifier not working"); - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(SS); + continue; + } + + if (Next.is(tok::annot_template_id) && + static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue()) + ->Kind == TNK_Concept_template && + GetLookAheadToken(2).isOneOf(tok::kw_auto, tok::kw_decltype)) { + DS.getTypeSpecScope() = SS; + // This is a qualified placeholder-specifier, e.g., ::C<int> auto ... + // Consume the scope annotation and continue to consume the template-id + // as a placeholder-specifier. + ConsumeAnnotationToken(); continue; } @@ -3235,6 +3248,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // C++ doesn't have implicit int. Diagnose it as a typo w.r.t. to the // typename. if (!TypeRep) { + if (TryAnnotateTypeConstraint()) + goto DoneWithDeclSpec; + if (isTypeConstraintAnnotation()) + continue; // Eat the scope spec so the identifier is current. ConsumeAnnotationToken(); ParsedAttributesWithRange Attrs(AttrFactory); @@ -3384,6 +3401,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // If this is not a typedef name, don't parse it as part of the declspec, // it must be an implicit int or an error. if (!TypeRep) { + if (TryAnnotateTypeConstraint()) + goto DoneWithDeclSpec; + if (isTypeConstraintAnnotation()) + continue; ParsedAttributesWithRange Attrs(AttrFactory); if (ParseImplicitInt(DS, nullptr, TemplateInfo, AS, DSContext, Attrs)) { if (!Attrs.empty()) { @@ -3433,9 +3454,51 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, continue; } - // type-name + // type-name or placeholder-specifier case tok::annot_template_id: { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); + if (TemplateId->Kind == TNK_Concept_template) { + if (NextToken().is(tok::identifier)) { + Diag(Loc, diag::err_placeholder_expected_auto_or_decltype_auto) + << FixItHint::CreateInsertion(NextToken().getLocation(), "auto"); + // Attempt to continue as if 'auto' was placed here. + isInvalid = DS.SetTypeSpecType(TST_auto, Loc, PrevSpec, DiagID, + TemplateId, Policy); + break; + } + if (!NextToken().isOneOf(tok::kw_auto, tok::kw_decltype)) + goto DoneWithDeclSpec; + ConsumeAnnotationToken(); + SourceLocation AutoLoc = Tok.getLocation(); + if (TryConsumeToken(tok::kw_decltype)) { + BalancedDelimiterTracker Tracker(*this, tok::l_paren); + if (Tracker.consumeOpen()) { + // Something like `void foo(Iterator decltype i)` + Diag(Tok, diag::err_expected) << tok::l_paren; + } else { + if (!TryConsumeToken(tok::kw_auto)) { + // Something like `void foo(Iterator decltype(int) i)` + Tracker.skipToEnd(); + Diag(Tok, diag::err_placeholder_expected_auto_or_decltype_auto) + << FixItHint::CreateReplacement(SourceRange(AutoLoc, + Tok.getLocation()), + "auto"); + } else { + Tracker.consumeClose(); + } + } + ConsumedEnd = Tok.getLocation(); + // Even if something went wrong above, continue as if we've seen + // `decltype(auto)`. + isInvalid = DS.SetTypeSpecType(TST_decltype_auto, Loc, PrevSpec, + DiagID, TemplateId, Policy); + } else { + isInvalid = DS.SetTypeSpecType(TST_auto, Loc, PrevSpec, DiagID, + TemplateId, Policy); + } + break; + } + if (TemplateId->Kind != TNK_Type_template && TemplateId->Kind != TNK_Undeclared_template) { // This template-id does not refer to a type name, so we're @@ -3448,12 +3511,13 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // constructor declaration. if (getLangOpts().CPlusPlus && DSContext == DeclSpecContext::DSC_class && Actions.isCurrentClassName(*TemplateId->Name, getCurScope()) && - isConstructorDeclarator(TemplateId->SS.isEmpty())) + isConstructorDeclarator(/*Unqualified=*/true)) goto DoneWithDeclSpec; // Turn the template-id annotation token into a type annotation // token, then try again to parse it as a type-specifier. - AnnotateTemplateIdTokenAsType(); + CXXScopeSpec SS; + AnnotateTemplateIdTokenAsType(SS); continue; } @@ -3617,7 +3681,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, ConsumedEnd = ExplicitLoc; ConsumeToken(); // kw_explicit if (Tok.is(tok::l_paren)) { - if (getLangOpts().CPlusPlus2a) { + if (getLangOpts().CPlusPlus2a || isExplicitBool() == TPResult::True) { + Diag(Tok.getLocation(), getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_explicit_bool + : diag::ext_explicit_bool); + ExprResult ExplicitExpr(static_cast<Expr *>(nullptr)); BalancedDelimiterTracker Tracker(*this, tok::l_paren); Tracker.consumeOpen(); @@ -3630,8 +3698,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, Actions.ActOnExplicitBoolSpecifier(ExplicitExpr.get()); } else Tracker.skipToEnd(); - } else + } else { Diag(Tok.getLocation(), diag::warn_cxx2a_compat_explicit_bool); + } } isInvalid = DS.setFunctionSpecExplicit(ExplicitLoc, PrevSpec, DiagID, ExplicitSpec, CloseParenLoc); @@ -6021,11 +6090,12 @@ void Parser::ParseDirectDeclarator(Declarator &D) { while (1) { if (Tok.is(tok::l_paren)) { + bool IsFunctionDeclaration = D.isFunctionDeclaratorAFunctionDeclaration(); // Enter function-declaration scope, limiting any declarators to the // function prototype scope, including parameter declarators. ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope|Scope::DeclScope| - (D.isFunctionDeclaratorAFunctionDeclaration() + (IsFunctionDeclaration ? Scope::FunctionDeclarationScope : 0)); // The paren may be part of a C++ direct initializer, eg. "int x(1);". @@ -6044,7 +6114,12 @@ void Parser::ParseDirectDeclarator(Declarator &D) { ParsedAttributes attrs(AttrFactory); BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); + if (IsFunctionDeclaration) + Actions.ActOnStartFunctionDeclarationDeclarator(D, + TemplateParameterDepth); ParseFunctionDeclarator(D, attrs, T, IsAmbiguous); + if (IsFunctionDeclaration) + Actions.ActOnFinishFunctionDeclarationDeclarator(D); PrototypeScope.Exit(); } else if (Tok.is(tok::l_square)) { ParseBracketDeclarator(D); @@ -6360,7 +6435,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, ProhibitAttributes(FnAttrs); } else { if (Tok.isNot(tok::r_paren)) - ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, + ParseParameterDeclarationClause(D.getContext(), FirstArgAttrs, ParamInfo, EllipsisLoc); else if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -6578,9 +6653,9 @@ void Parser::ParseFunctionDeclaratorIdentifierList( /// after the opening parenthesis. This function will not parse a K&R-style /// identifier list. /// -/// D is the declarator being parsed. If FirstArgAttrs is non-null, then the -/// caller parsed those arguments immediately after the open paren - they should -/// be considered to be part of the first parameter. +/// DeclContext is the context of the declarator being parsed. If FirstArgAttrs +/// is non-null, then the caller parsed those attributes immediately after the +/// open paren - they should be considered to be part of the first parameter. /// /// After returning, ParamInfo will hold the parsed parameters. EllipsisLoc will /// be the location of the ellipsis, if any was parsed. @@ -6606,7 +6681,7 @@ void Parser::ParseFunctionDeclaratorIdentifierList( /// [C++11] attribute-specifier-seq parameter-declaration /// void Parser::ParseParameterDeclarationClause( - Declarator &D, + DeclaratorContext DeclaratorCtx, ParsedAttributes &FirstArgAttrs, SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo, SourceLocation &EllipsisLoc) { @@ -6655,9 +6730,11 @@ void Parser::ParseParameterDeclarationClause( // "LambdaExprParameterContext", because we must accept either // 'declarator' or 'abstract-declarator' here. Declarator ParmDeclarator( - DS, D.getContext() == DeclaratorContext::LambdaExprContext - ? DeclaratorContext::LambdaExprParameterContext - : DeclaratorContext::PrototypeContext); + DS, DeclaratorCtx == DeclaratorContext::RequiresExprContext + ? DeclaratorContext::RequiresExprContext + : DeclaratorCtx == DeclaratorContext::LambdaExprContext + ? DeclaratorContext::LambdaExprParameterContext + : DeclaratorContext::PrototypeContext); ParseDeclarator(ParmDeclarator); // Parse GNU attributes, if present. @@ -6711,7 +6788,7 @@ void Parser::ParseParameterDeclarationClause( SourceLocation EqualLoc = Tok.getLocation(); // Parse the default argument - if (D.getContext() == DeclaratorContext::MemberContext) { + if (DeclaratorCtx == DeclaratorContext::MemberContext) { // If we're inside a class definition, cache the tokens // corresponding to the default argument. We'll actually parse // them when we see the end of the class definition. diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 081d4d8b12092..f872aa3a950c9 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1142,7 +1142,7 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, if (TemplateId->Kind == TNK_Type_template || TemplateId->Kind == TNK_Dependent_template_name || TemplateId->Kind == TNK_Undeclared_template) { - AnnotateTemplateIdTokenAsType(/*IsClassName*/true); + AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true); assert(Tok.is(tok::annot_typename) && "template-id -> type failed"); ParsedType Type = getTypeAnnotation(Tok); @@ -1193,7 +1193,7 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, TemplateName)) return true; if (TNK == TNK_Type_template || TNK == TNK_Dependent_template_name) - AnnotateTemplateIdTokenAsType(/*IsClassName*/true); + AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true); // If we didn't end up with a typename token, there's nothing more we // can do. @@ -1826,7 +1826,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, TemplateInfo.Kind == ParsedTemplateInfo::NonTemplate)) { ProhibitAttributes(attrs); TypeResult = Actions.ActOnTagTemplateIdType(TUK, TagType, StartLoc, - TemplateId->SS, + SS, TemplateId->TemplateKWLoc, TemplateId->Template, TemplateId->TemplateNameLoc, @@ -1876,7 +1876,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // Build the class template specialization. TagOrTempResult = Actions.ActOnClassTemplateSpecialization( getCurScope(), TagType, TUK, StartLoc, DS.getModulePrivateSpecLoc(), - *TemplateId, attrs, + SS, *TemplateId, attrs, MultiTemplateParamsArg(TemplateParams ? &(*TemplateParams)[0] : nullptr, TemplateParams ? TemplateParams->size() : 0), @@ -2642,6 +2642,8 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, } ParsingDeclarator DeclaratorInfo(*this, DS, DeclaratorContext::MemberContext); + if (TemplateInfo.TemplateParams) + DeclaratorInfo.setTemplateParameterLists(TemplateParams); VirtSpecifiers VS; // Hold late-parsed attributes so we can attach a Decl to them later. @@ -3520,7 +3522,7 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) { if (TemplateId && (TemplateId->Kind == TNK_Type_template || TemplateId->Kind == TNK_Dependent_template_name || TemplateId->Kind == TNK_Undeclared_template)) { - AnnotateTemplateIdTokenAsType(/*IsClassName*/true); + AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true); assert(Tok.is(tok::annot_typename) && "template-id -> type failed"); TemplateTypeTy = getTypeAnnotation(Tok); ConsumeAnnotationToken(); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 1442df046bb93..1eb3ad6afd1cb 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -234,7 +234,7 @@ ExprResult Parser::ParseCaseExpression(SourceLocation CaseLoc) { /// \endverbatim ExprResult Parser::ParseConstraintExpression() { EnterExpressionEvaluationContext ConstantEvaluated( - Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); + Actions, Sema::ExpressionEvaluationContext::Unevaluated); ExprResult LHS(ParseCastExpression(AnyCastExpr)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr)); if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get())) { @@ -256,7 +256,7 @@ ExprResult Parser::ParseConstraintExpression() { ExprResult Parser::ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause) { EnterExpressionEvaluationContext ConstantEvaluated( - Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); + Actions, Sema::ExpressionEvaluationContext::Unevaluated); bool NotPrimaryExpression = false; auto ParsePrimary = [&] () { ExprResult E = ParseCastExpression(PrimaryExprOnly, @@ -756,6 +756,7 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback { /// [C++11] user-defined-literal /// '(' expression ')' /// [C11] generic-selection +/// [C++2a] requires-expression /// '__func__' [C99 6.4.2.2] /// [GNU] '__FUNCTION__' /// [MS] '__FUNCDNAME__' @@ -1530,7 +1531,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, CXXScopeSpec SS; ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false); - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(SS); return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, isTypeCast, isVectorLiteral, NotPrimaryExpression); @@ -1548,7 +1549,8 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, // We have a template-id that we know refers to a type, // translate it into a type and continue parsing as a cast // expression. - AnnotateTemplateIdTokenAsType(); + CXXScopeSpec SS; + AnnotateTemplateIdTokenAsType(SS); return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, isTypeCast, isVectorLiteral, NotPrimaryExpression); @@ -1600,6 +1602,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, *NotPrimaryExpression = true; return ParseCXXDeleteExpression(false, Tok.getLocation()); + case tok::kw_requires: // [C++2a] requires-expression + return ParseRequiresExpression(); + case tok::kw_noexcept: { // [C++0x] 'noexcept' '(' expression ')' if (NotPrimaryExpression) *NotPrimaryExpression = true; diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index e685d5ea8a9cc..036eabb94dd7e 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -11,7 +11,9 @@ //===----------------------------------------------------------------------===// #include "clang/Parse/Parser.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/ExprCXX.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Parse/ParseDiagnostic.h" @@ -165,13 +167,6 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, return false; } - if (Tok.is(tok::annot_template_id)) { - // If the current token is an annotated template id, it may already have - // a scope specifier. Restore it. - TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); - SS = TemplateId->SS; - } - // Has to happen before any "return false"s in this function. bool CheckForDestructor = false; if (MayBePseudoDestructor && *MayBePseudoDestructor) { @@ -1306,9 +1301,9 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( Actions.RecordParsingTemplateParameterDepth( CurTemplateDepthTracker.getOriginalDepth()); - ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc); - - // For a generic lambda, each 'auto' within the parameter declaration + ParseParameterDeclarationClause(D.getContext(), Attr, ParamInfo, + EllipsisLoc); + // For a generic lambda, each 'auto' within the parameter declaration // clause creates a template type parameter, so increment the depth. // If we've parsed any explicit template parameters, then the depth will // have already been incremented. So we make sure that at most a single @@ -2405,7 +2400,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, : Id.OperatorFunctionId.Operator; TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create( - SS, TemplateKWLoc, Id.StartLocation, TemplateII, OpKind, Template, TNK, + TemplateKWLoc, Id.StartLocation, TemplateII, OpKind, Template, TNK, LAngleLoc, RAngleLoc, TemplateArgs, TemplateIds); Id.setTemplateId(TemplateId); @@ -3262,6 +3257,324 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) { return Actions.ActOnCXXDelete(Start, UseGlobal, ArrayDelete, Operand.get()); } +/// ParseRequiresExpression - Parse a C++2a requires-expression. +/// C++2a [expr.prim.req]p1 +/// A requires-expression provides a concise way to express requirements on +/// template arguments. A requirement is one that can be checked by name +/// lookup (6.4) or by checking properties of types and expressions. +/// +/// requires-expression: +/// 'requires' requirement-parameter-list[opt] requirement-body +/// +/// requirement-parameter-list: +/// '(' parameter-declaration-clause[opt] ')' +/// +/// requirement-body: +/// '{' requirement-seq '}' +/// +/// requirement-seq: +/// requirement +/// requirement-seq requirement +/// +/// requirement: +/// simple-requirement +/// type-requirement +/// compound-requirement +/// nested-requirement +ExprResult Parser::ParseRequiresExpression() { + assert(Tok.is(tok::kw_requires) && "Expected 'requires' keyword"); + SourceLocation RequiresKWLoc = ConsumeToken(); // Consume 'requires' + + llvm::SmallVector<ParmVarDecl *, 2> LocalParameterDecls; + if (Tok.is(tok::l_paren)) { + // requirement parameter list is present. + ParseScope LocalParametersScope(this, Scope::FunctionPrototypeScope | + Scope::DeclScope); + BalancedDelimiterTracker Parens(*this, tok::l_paren); + Parens.consumeOpen(); + if (!Tok.is(tok::r_paren)) { + ParsedAttributes FirstArgAttrs(getAttrFactory()); + SourceLocation EllipsisLoc; + llvm::SmallVector<DeclaratorChunk::ParamInfo, 2> LocalParameters; + DiagnosticErrorTrap Trap(Diags); + ParseParameterDeclarationClause(DeclaratorContext::RequiresExprContext, + FirstArgAttrs, LocalParameters, + EllipsisLoc); + if (EllipsisLoc.isValid()) + Diag(EllipsisLoc, diag::err_requires_expr_parameter_list_ellipsis); + for (auto &ParamInfo : LocalParameters) + LocalParameterDecls.push_back(cast<ParmVarDecl>(ParamInfo.Param)); + if (Trap.hasErrorOccurred()) + SkipUntil(tok::r_paren, StopBeforeMatch); + } + Parens.consumeClose(); + } + + BalancedDelimiterTracker Braces(*this, tok::l_brace); + if (Braces.expectAndConsume()) + return ExprError(); + + // Start of requirement list + llvm::SmallVector<concepts::Requirement *, 2> Requirements; + + // C++2a [expr.prim.req]p2 + // Expressions appearing within a requirement-body are unevaluated operands. + EnterExpressionEvaluationContext Ctx( + Actions, Sema::ExpressionEvaluationContext::Unevaluated); + + ParseScope BodyScope(this, Scope::DeclScope); + RequiresExprBodyDecl *Body = Actions.ActOnStartRequiresExpr( + RequiresKWLoc, LocalParameterDecls, getCurScope()); + + if (Tok.is(tok::r_brace)) { + // Grammar does not allow an empty body. + // requirement-body: + // { requirement-seq } + // requirement-seq: + // requirement + // requirement-seq requirement + Diag(Tok, diag::err_empty_requires_expr); + // Continue anyway and produce a requires expr with no requirements. + } else { + while (!Tok.is(tok::r_brace)) { + switch (Tok.getKind()) { + case tok::l_brace: { + // Compound requirement + // C++ [expr.prim.req.compound] + // compound-requirement: + // '{' expression '}' 'noexcept'[opt] + // return-type-requirement[opt] ';' + // return-type-requirement: + // trailing-return-type + // '->' cv-qualifier-seq[opt] constrained-parameter + // cv-qualifier-seq[opt] abstract-declarator[opt] + BalancedDelimiterTracker ExprBraces(*this, tok::l_brace); + ExprBraces.consumeOpen(); + ExprResult Expression = + Actions.CorrectDelayedTyposInExpr(ParseExpression()); + if (!Expression.isUsable()) { + ExprBraces.skipToEnd(); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (ExprBraces.consumeClose()) + ExprBraces.skipToEnd(); + + concepts::Requirement *Req = nullptr; + SourceLocation NoexceptLoc; + TryConsumeToken(tok::kw_noexcept, NoexceptLoc); + if (Tok.is(tok::semi)) { + Req = Actions.ActOnCompoundRequirement(Expression.get(), NoexceptLoc); + if (Req) + Requirements.push_back(Req); + break; + } + if (!TryConsumeToken(tok::arrow)) + // User probably forgot the arrow, remind them and try to continue. + Diag(Tok, diag::err_requires_expr_missing_arrow) + << FixItHint::CreateInsertion(Tok.getLocation(), "->"); + // Try to parse a 'type-constraint' + CXXScopeSpec SS; + if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), + /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + // If this is not a type-constraint, + // then this scope-spec is part of + // the typename of a non-type + // template parameter + /*IsTypename=*/true, + /*LastII=*/nullptr, + // We won't find concepts in + // non-namespaces anyway, so might as + // well parse this correctly for + // possible type names. + /*OnlyNamespace=*/false, + /*SuppressDiagnostic=*/true)) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (TryAnnotateTypeConstraint()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (!isTypeConstraintAnnotation()) { + Diag(Tok, diag::err_requires_expr_expected_type_constraint); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (Tok.is(tok::annot_cxxscope)) + ConsumeAnnotationToken(); + + Req = Actions.ActOnCompoundRequirement( + Expression.get(), NoexceptLoc, SS, takeTemplateIdAnnotation(Tok), + TemplateParameterDepth); + ConsumeAnnotationToken(); + if (Req) + Requirements.push_back(Req); + break; + } + default: { + bool PossibleRequiresExprInSimpleRequirement = false; + if (Tok.is(tok::kw_requires)) { + auto IsNestedRequirement = [&] { + RevertingTentativeParsingAction TPA(*this); + ConsumeToken(); // 'requires' + if (Tok.is(tok::l_brace)) + // This is a requires expression + // requires (T t) { + // requires { t++; }; + // ... ^ + // } + return false; + if (Tok.is(tok::l_paren)) { + // This might be the parameter list of a requires expression + ConsumeParen(); + auto Res = TryParseParameterDeclarationClause(); + if (Res != TPResult::False) { + // Skip to the closing parenthesis + // FIXME: Don't traverse these tokens twice (here and in + // TryParseParameterDeclarationClause). + unsigned Depth = 1; + while (Depth != 0) { + if (Tok.is(tok::l_paren)) + Depth++; + else if (Tok.is(tok::r_paren)) + Depth--; + ConsumeAnyToken(); + } + // requires (T t) { + // requires () ? + // ... ^ + // - OR - + // requires (int x) ? + // ... ^ + // } + if (Tok.is(tok::l_brace)) + // requires (...) { + // ^ - a requires expression as a + // simple-requirement. + return false; + } + } + return true; + }; + if (IsNestedRequirement()) { + ConsumeToken(); + // Nested requirement + // C++ [expr.prim.req.nested] + // nested-requirement: + // 'requires' constraint-expression ';' + ExprResult ConstraintExpr = + Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression()); + if (ConstraintExpr.isInvalid() || !ConstraintExpr.isUsable()) { + SkipUntil(tok::semi, tok::r_brace, + SkipUntilFlags::StopBeforeMatch); + break; + } + if (auto *Req = + Actions.ActOnNestedRequirement(ConstraintExpr.get())) + Requirements.push_back(Req); + else { + SkipUntil(tok::semi, tok::r_brace, + SkipUntilFlags::StopBeforeMatch); + break; + } + break; + } else + PossibleRequiresExprInSimpleRequirement = true; + } else if (Tok.is(tok::kw_typename)) { + // This might be 'typename T::value_type;' (a type requirement) or + // 'typename T::value_type{};' (a simple requirement). + TentativeParsingAction TPA(*this); + + // We need to consume the typename to allow 'requires { typename a; }' + SourceLocation TypenameKWLoc = ConsumeToken(); + if (TryAnnotateCXXScopeToken()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + CXXScopeSpec SS; + if (Tok.is(tok::annot_cxxscope)) { + Actions.RestoreNestedNameSpecifierAnnotation( + Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS); + ConsumeAnnotationToken(); + } + + if (Tok.isOneOf(tok::identifier, tok::annot_template_id) && + !NextToken().isOneOf(tok::l_brace, tok::l_paren)) { + TPA.Commit(); + SourceLocation NameLoc = Tok.getLocation(); + IdentifierInfo *II = nullptr; + TemplateIdAnnotation *TemplateId = nullptr; + if (Tok.is(tok::identifier)) { + II = Tok.getIdentifierInfo(); + ConsumeToken(); + } else { + TemplateId = takeTemplateIdAnnotation(Tok); + ConsumeAnnotationToken(); + } + + if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, SS, + NameLoc, II, + TemplateId)) { + Requirements.push_back(Req); + } + break; + } + TPA.Revert(); + } + // Simple requirement + // C++ [expr.prim.req.simple] + // simple-requirement: + // expression ';' + SourceLocation StartLoc = Tok.getLocation(); + ExprResult Expression = + Actions.CorrectDelayedTyposInExpr(ParseExpression()); + if (!Expression.isUsable()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (!Expression.isInvalid() && PossibleRequiresExprInSimpleRequirement) + Diag(StartLoc, diag::warn_requires_expr_in_simple_requirement) + << FixItHint::CreateInsertion(StartLoc, "requires"); + if (auto *Req = Actions.ActOnSimpleRequirement(Expression.get())) + Requirements.push_back(Req); + else { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + // User may have tried to put some compound requirement stuff here + if (Tok.is(tok::kw_noexcept)) { + Diag(Tok, diag::err_requires_expr_simple_requirement_noexcept) + << FixItHint::CreateInsertion(StartLoc, "{") + << FixItHint::CreateInsertion(Tok.getLocation(), "}"); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + break; + } + } + if (ExpectAndConsumeSemi(diag::err_expected_semi_requirement)) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + TryConsumeToken(tok::semi); + break; + } + } + if (Requirements.empty()) { + // Don't emit an empty requires expr here to avoid confusing the user with + // other diagnostics quoting an empty requires expression they never + // wrote. + Braces.consumeClose(); + Actions.ActOnFinishRequiresExpr(); + return ExprError(); + } + } + Braces.consumeClose(); + Actions.ActOnFinishRequiresExpr(); + return Actions.ActOnRequiresExpr(RequiresKWLoc, Body, LocalParameterDecls, + Requirements, Braces.getCloseLocation()); +} + static TypeTrait TypeTraitFromTokKind(tok::TokenKind kind) { switch (kind) { default: llvm_unreachable("Not a known type trait"); diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 1b9301b6591dc..3bc4e3596f120 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -240,6 +240,8 @@ Decl *Parser::ParseSingleDeclarationAfterTemplate( // Parse the declarator. ParsingDeclarator DeclaratorInfo(*this, DS, (DeclaratorContext)Context); + if (TemplateInfo.TemplateParams) + DeclaratorInfo.setTemplateParameterLists(*TemplateInfo.TemplateParams); ParseDeclarator(DeclaratorInfo); // Error parsing the declarator? if (!DeclaratorInfo.hasName()) { @@ -499,10 +501,7 @@ Parser::ParseTemplateParameterList(const unsigned Depth, /// Determine whether the parser is at the start of a template /// type parameter. -/// \param ScopeError will receive true if there was an error parsing a -/// scope specifier at the current location. -bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { - ScopeError = false; +Parser::TPResult Parser::isStartOfTemplateTypeParameter() { if (Tok.is(tok::kw_class)) { // "class" may be the start of an elaborated-type-specifier or a // type-parameter. Per C++ [temp.param]p3, we prefer the type-parameter. @@ -512,7 +511,7 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { case tok::greater: case tok::greatergreater: case tok::ellipsis: - return true; + return TPResult::True; case tok::identifier: // This may be either a type-parameter or an elaborated-type-specifier. @@ -520,7 +519,7 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { break; default: - return false; + return TPResult::False; } switch (GetLookAheadToken(2).getKind()) { @@ -528,51 +527,28 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { case tok::comma: case tok::greater: case tok::greatergreater: - return true; + return TPResult::True; default: - return false; + return TPResult::False; } } - bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); - CXXScopeSpec SS; - ScopeError = - ParseOptionalCXXScopeSpecifier(SS, ParsedType(), - /*EnteringContext=*/false, - /*MayBePseudoDestructor=*/nullptr, - // If this is not a type-constraint, then - // this scope-spec is part of the typename - // of a non-type template parameter - /*IsTypename=*/true, /*LastII=*/nullptr, - // We won't find concepts in - // non-namespaces anyway, so might as well - // parse this correctly for possible type - // names. - /*OnlyNamespace=*/false); - if (ScopeError) - return false; - if (TryAnnotateTypeConstraint(SS)) - return false; - bool IsTypeConstraint = isTypeConstraintAnnotation(); - if (!IsTypeConstraint && SS.isNotEmpty()) { - // This isn't a type-constraint but we've already parsed this scope - // specifier - annotate it. - AnnotateScopeToken(SS, /*isNewAnnotation=*/!WasScopeAnnotation); - return false; - } + if (TryAnnotateTypeConstraint()) + return TPResult::Error; - if (IsTypeConstraint && + if (isTypeConstraintAnnotation() && // Next token might be 'auto' or 'decltype', indicating that this // type-constraint is in fact part of a placeholder-type-specifier of a // non-type template parameter. - !NextToken().isOneOf(tok::kw_auto, tok::kw_decltype)) - return true; + !GetLookAheadToken(Tok.is(tok::annot_cxxscope) ? 2 : 1) + .isOneOf(tok::kw_auto, tok::kw_decltype)) + return TPResult::True; // 'typedef' is a reasonably-common typo/thinko for 'typename', and is // ill-formed otherwise. if (Tok.isNot(tok::kw_typename) && Tok.isNot(tok::kw_typedef)) - return false; + return TPResult::False; // C++ [temp.param]p2: // There is no semantic difference between class and typename in a @@ -592,17 +568,17 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { case tok::greater: case tok::greatergreater: case tok::ellipsis: - return true; + return TPResult::True; case tok::kw_typename: case tok::kw_typedef: case tok::kw_class: // These indicate that a comma was missed after a type parameter, not that // we have found a non-type parameter. - return true; + return TPResult::True; default: - return false; + return TPResult::False; } } @@ -627,13 +603,9 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { /// typename /// NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { - // We could be facing a type-constraint, which (could) start a type parameter. - // Annotate it now (we might end up not using it if we determine this - // type-constraint is in fact part of a placeholder-type-specifier of a - // non-type template parameter. - bool ScopeError; - if (isStartOfTemplateTypeParameter(ScopeError)) { + switch (isStartOfTemplateTypeParameter()) { + case TPResult::True: // Is there just a typo in the input code? ('typedef' instead of // 'typename') if (Tok.is(tok::kw_typedef)) { @@ -649,8 +621,10 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { } return ParseTypeParameter(Depth, Position); - } - if (ScopeError) { + case TPResult::False: + break; + + case TPResult::Error: { // We return an invalid parameter as opposed to null to avoid having bogus // diagnostics about an empty template parameter list. // FIXME: Fix ParseTemplateParameterList to better handle nullptr results @@ -670,6 +644,11 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { StopAtSemi | StopBeforeMatch); return ErrorParam; } + + case TPResult::Ambiguous: + llvm_unreachable("template param classification can't be ambiguous"); + } + if (Tok.is(tok::kw_template)) return ParseTemplateTemplateParameter(Depth, Position); @@ -682,15 +661,15 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { /// Check whether the current token is a template-id annotation denoting a /// type-constraint. bool Parser::isTypeConstraintAnnotation() { - if (Tok.isNot(tok::annot_template_id)) + const Token &T = Tok.is(tok::annot_cxxscope) ? NextToken() : Tok; + if (T.isNot(tok::annot_template_id)) return false; const auto *ExistingAnnot = - static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + static_cast<TemplateIdAnnotation *>(T.getAnnotationValue()); return ExistingAnnot->Kind == TNK_Concept_template; } -/// Try parsing a type-constraint construct at the current location, after the -/// optional scope specifier. +/// Try parsing a type-constraint at the current location. /// /// type-constraint: /// nested-name-specifier[opt] concept-name @@ -698,35 +677,61 @@ bool Parser::isTypeConstraintAnnotation() { /// '<' template-argument-list[opt] '>'[opt] /// /// \returns true if an error occurred, and false otherwise. -bool Parser::TryAnnotateTypeConstraint(CXXScopeSpec &SS) { - if (!getLangOpts().ConceptsTS || Tok.isNot(tok::identifier)) +bool Parser::TryAnnotateTypeConstraint() { + if (!getLangOpts().CPlusPlus2a) return false; + CXXScopeSpec SS; + bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); + if (ParseOptionalCXXScopeSpecifier( + SS, ParsedType(), + /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + // If this is not a type-constraint, then + // this scope-spec is part of the typename + // of a non-type template parameter + /*IsTypename=*/true, /*LastII=*/nullptr, + // We won't find concepts in + // non-namespaces anyway, so might as well + // parse this correctly for possible type + // names. + /*OnlyNamespace=*/false)) + return true; - UnqualifiedId PossibleConceptName; - PossibleConceptName.setIdentifier(Tok.getIdentifierInfo(), - Tok.getLocation()); - - TemplateTy PossibleConcept; - bool MemberOfUnknownSpecialization = false; - auto TNK = Actions.isTemplateName(getCurScope(), SS, - /*hasTemplateKeyword=*/false, - PossibleConceptName, - /*ObjectType=*/ParsedType(), - /*EnteringContext=*/false, - PossibleConcept, - MemberOfUnknownSpecialization); - assert(!MemberOfUnknownSpecialization - && "Member when we only allowed namespace scope qualifiers??"); - if (!PossibleConcept || TNK != TNK_Concept_template) - return false; + if (Tok.is(tok::identifier)) { + UnqualifiedId PossibleConceptName; + PossibleConceptName.setIdentifier(Tok.getIdentifierInfo(), + Tok.getLocation()); + + TemplateTy PossibleConcept; + bool MemberOfUnknownSpecialization = false; + auto TNK = Actions.isTemplateName(getCurScope(), SS, + /*hasTemplateKeyword=*/false, + PossibleConceptName, + /*ObjectType=*/ParsedType(), + /*EnteringContext=*/false, + PossibleConcept, + MemberOfUnknownSpecialization); + if (MemberOfUnknownSpecialization || !PossibleConcept || + TNK != TNK_Concept_template) { + if (SS.isNotEmpty()) + AnnotateScopeToken(SS, !WasScopeAnnotation); + return false; + } - // At this point we're sure we're dealing with a constrained parameter. It - // may or may not have a template parameter list following the concept name. - return AnnotateTemplateIdToken(PossibleConcept, TNK, SS, - /*TemplateKWLoc=*/SourceLocation(), - PossibleConceptName, - /*AllowTypeAnnotation=*/false, - /*TypeConstraint=*/true); + // At this point we're sure we're dealing with a constrained parameter. It + // may or may not have a template parameter list following the concept + // name. + if (AnnotateTemplateIdToken(PossibleConcept, TNK, SS, + /*TemplateKWLoc=*/SourceLocation(), + PossibleConceptName, + /*AllowTypeAnnotation=*/false, + /*TypeConstraint=*/true)) + return true; + } + + if (SS.isNotEmpty()) + AnnotateScopeToken(SS, !WasScopeAnnotation); + return false; } /// ParseTypeParameter - Parse a template type parameter (C++ [temp.param]). @@ -739,13 +744,17 @@ bool Parser::TryAnnotateTypeConstraint(CXXScopeSpec &SS) { /// 'typename' ...[opt][C++0x] identifier[opt] /// 'typename' identifier[opt] '=' type-id NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { - assert(Tok.isOneOf(tok::kw_class, tok::kw_typename, tok::annot_template_id) && + assert((Tok.isOneOf(tok::kw_class, tok::kw_typename) || + isTypeConstraintAnnotation()) && "A type-parameter starts with 'class', 'typename' or a " "type-constraint"); + CXXScopeSpec TypeConstraintSS; TemplateIdAnnotation *TypeConstraint = nullptr; bool TypenameKeyword = false; SourceLocation KeyLoc; + ParseOptionalCXXScopeSpecifier(TypeConstraintSS, nullptr, + /*EnteringContext*/ false); if (Tok.is(tok::annot_template_id)) { // Consume the 'type-constraint'. TypeConstraint = @@ -754,6 +763,9 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { "stray non-concept template-id annotation"); KeyLoc = ConsumeAnnotationToken(); } else { + assert(TypeConstraintSS.isEmpty() && + "expected type constraint after scope specifier"); + // Consume the 'class' or 'typename' keyword. TypenameKeyword = Tok.is(tok::kw_typename); KeyLoc = ConsumeToken(); @@ -795,7 +807,8 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { ParsedType DefaultArg; if (TryConsumeToken(tok::equal, EqualLoc)) DefaultArg = ParseTypeName(/*Range=*/nullptr, - DeclaratorContext::TemplateTypeArgContext).get(); + DeclaratorContext::TemplateTypeArgContext) + .get(); NamedDecl *NewDecl = Actions.ActOnTypeParameter(getCurScope(), TypenameKeyword, EllipsisLoc, @@ -804,10 +817,11 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { DefaultArg, TypeConstraint != nullptr); - if (TypeConstraint) - Actions.ActOnTypeConstraint(TypeConstraint, + if (TypeConstraint) { + Actions.ActOnTypeConstraint(TypeConstraintSS, TypeConstraint, cast<TemplateTypeParmDecl>(NewDecl), EllipsisLoc); + } return NewDecl; } @@ -1331,8 +1345,8 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, : TemplateName.OperatorFunctionId.Operator; TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create( - SS, TemplateKWLoc, TemplateNameLoc, TemplateII, OpKind, Template, TNK, - LAngleLoc, RAngleLoc, TemplateArgs, TemplateIds); + TemplateKWLoc, TemplateNameLoc, TemplateII, OpKind, Template, TNK, + LAngleLoc, RAngleLoc, TemplateArgs, TemplateIds); Tok.setAnnotationValue(TemplateId); if (TemplateKWLoc.isValid()) @@ -1357,11 +1371,14 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, /// a type annotation token will still be created, but will have a /// NULL type pointer to signify an error. /// +/// \param SS The scope specifier appearing before the template-id, if any. +/// /// \param IsClassName Is this template-id appearing in a context where we /// know it names a class, such as in an elaborated-type-specifier or /// base-specifier? ('typename' and 'template' are unneeded and disallowed /// in those contexts.) -void Parser::AnnotateTemplateIdTokenAsType(bool IsClassName) { +void Parser::AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS, + bool IsClassName) { assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens"); TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); @@ -1375,7 +1392,7 @@ void Parser::AnnotateTemplateIdTokenAsType(bool IsClassName) { TypeResult Type = Actions.ActOnTemplateIdType(getCurScope(), - TemplateId->SS, + SS, TemplateId->TemplateKWLoc, TemplateId->Template, TemplateId->Name, @@ -1388,8 +1405,8 @@ void Parser::AnnotateTemplateIdTokenAsType(bool IsClassName) { // Create the new "type" annotation token. Tok.setKind(tok::annot_typename); setTypeAnnotation(Tok, Type.isInvalid() ? nullptr : Type.get()); - if (TemplateId->SS.isNotEmpty()) // it was a C++ qualified type name. - Tok.setLocation(TemplateId->SS.getBeginLoc()); + if (SS.isNotEmpty()) // it was a C++ qualified type name. + Tok.setLocation(SS.getBeginLoc()); // End location stays the same // Replace the template-id annotation token, and possible the scope-specifier diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 4d69fb4693fb8..ad0a15b0c8a6d 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -202,9 +202,7 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() { } } - if (Tok.isOneOf(tok::identifier, tok::coloncolon, tok::kw_decltype, - tok::annot_template_id) && - TryAnnotateCXXScopeToken()) + if (TryAnnotateOptionalCXXScopeToken()) return TPResult::Error; if (Tok.is(tok::annot_cxxscope)) ConsumeAnnotationToken(); @@ -785,9 +783,8 @@ Parser::isCXX11AttributeSpecifier(bool Disambiguate, Parser::TPResult Parser::TryParsePtrOperatorSeq() { while (true) { - if (Tok.isOneOf(tok::coloncolon, tok::identifier)) - if (TryAnnotateCXXScopeToken(true)) - return TPResult::Error; + if (TryAnnotateOptionalCXXScopeToken(true)) + return TPResult::Error; if (Tok.isOneOf(tok::star, tok::amp, tok::caret, tok::ampamp) || (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) { @@ -1316,6 +1313,18 @@ public: Parser::TPResult Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, bool *InvalidAsDeclSpec) { + auto IsPlaceholderSpecifier = [&] (TemplateIdAnnotation *TemplateId, + int Lookahead) { + // We have a placeholder-constraint (we check for 'auto' or 'decltype' to + // distinguish 'C<int>;' from 'C<int> auto c = 1;') + return TemplateId->Kind == TNK_Concept_template && + GetLookAheadToken(Lookahead + 1).isOneOf(tok::kw_auto, tok::kw_decltype, + // If we have an identifier here, the user probably forgot the + // 'auto' in the placeholder constraint, e.g. 'C<int> x = 2;' + // This will be diagnosed nicely later, so disambiguate as a + // declaration. + tok::identifier); + }; switch (Tok.getKind()) { case tok::identifier: { // Check for need to substitute AltiVec __vector keyword @@ -1519,10 +1528,12 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, *InvalidAsDeclSpec = NextToken().is(tok::l_paren); return TPResult::Ambiguous; } + if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/0)) + return TPResult::True; if (TemplateId->Kind != TNK_Type_template) return TPResult::False; CXXScopeSpec SS; - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(SS); assert(Tok.is(tok::annot_typename)); goto case_typename; } @@ -1532,6 +1543,13 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, if (TryAnnotateTypeOrScopeToken()) return TPResult::Error; if (!Tok.is(tok::annot_typename)) { + if (Tok.is(tok::annot_cxxscope) && + NextToken().is(tok::annot_template_id)) { + TemplateIdAnnotation *TemplateId = + takeTemplateIdAnnotation(NextToken()); + if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/1)) + return TPResult::True; + } // If the next token is an identifier or a type qualifier, then this // can't possibly be a valid expression either. if (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::identifier)) { @@ -2137,3 +2155,58 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) { return TPResult::Ambiguous; return TPResult::False; } + +/// Determine whether we might be looking at the '(' of a C++20 explicit(bool) +/// in an earlier language mode. +Parser::TPResult Parser::isExplicitBool() { + assert(Tok.is(tok::l_paren) && "expected to be looking at a '(' token"); + + RevertingTentativeParsingAction PA(*this); + ConsumeParen(); + + // We can only have 'explicit' on a constructor, conversion function, or + // deduction guide. The declarator of a deduction guide cannot be + // parenthesized, so we know this isn't a deduction guide. So the only + // thing we need to check for is some number of parens followed by either + // the current class name or 'operator'. + while (Tok.is(tok::l_paren)) + ConsumeParen(); + + if (TryAnnotateOptionalCXXScopeToken()) + return TPResult::Error; + + // Class-scope constructor and conversion function names can't really be + // qualified, but we get better diagnostics if we assume they can be. + CXXScopeSpec SS; + if (Tok.is(tok::annot_cxxscope)) { + Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(), + Tok.getAnnotationRange(), + SS); + ConsumeAnnotationToken(); + } + + // 'explicit(operator' might be explicit(bool) or the declaration of a + // conversion function, but it's probably a conversion function. + if (Tok.is(tok::kw_operator)) + return TPResult::Ambiguous; + + // If this can't be a constructor name, it can only be explicit(bool). + if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id)) + return TPResult::True; + if (!Actions.isCurrentClassName(Tok.is(tok::identifier) + ? *Tok.getIdentifierInfo() + : *takeTemplateIdAnnotation(Tok)->Name, + getCurScope(), &SS)) + return TPResult::True; + // Formally, we must have a right-paren after the constructor name to match + // the grammar for a constructor. But clang permits a parenthesized + // constructor declarator, so also allow a constructor declarator to follow + // with no ')' token after the constructor name. + if (!NextToken().is(tok::r_paren) && + !isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(), + /*DeductionGuide=*/false)) + return TPResult::True; + + // Might be explicit(bool) or a parenthesized constructor name. + return TPResult::Ambiguous; +} diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 4249de361b89e..0b778bd242776 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1136,6 +1136,7 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, // Poison SEH identifiers so they are flagged as illegal in function bodies. PoisonSEHIdentifiersRAIIObject PoisonSEHIdentifiers(*this, true); const DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo(); + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); // If this is C90 and the declspecs were completely missing, fudge in an // implicit int. We do this here because this is the only place where @@ -1262,6 +1263,15 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, // safe because we're always the sole owner. D.getMutableDeclSpec().abort(); + // With abbreviated function templates - we need to explicitly add depth to + // account for the implicit template parameter list induced by the template. + if (auto *Template = dyn_cast_or_null<FunctionTemplateDecl>(Res)) + if (Template->isAbbreviated() && + Template->getTemplateParameters()->getParam(0)->isImplicit()) + // First template parameter is implicit - meaning no explicit template + // parameter list was specified. + CurTemplateDepthTracker.addDepth(1); + if (TryConsumeToken(tok::equal)) { assert(getLangOpts().CPlusPlus && "Only C++ function definitions have '='"); @@ -1732,6 +1742,20 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC) { return ANK_Error; return ANK_Success; } + case Sema::NC_Concept: { + UnqualifiedId Id; + Id.setIdentifier(Name, NameLoc); + if (Next.is(tok::less)) + // We have a concept name followed by '<'. Consume the identifier token so + // we reach the '<' and annotate it. + ConsumeToken(); + if (AnnotateTemplateIdToken( + TemplateTy::make(Classification.getTemplateName()), + Classification.getTemplateNameKind(), SS, SourceLocation(), Id, + /*AllowTypeAnnotation=*/false, /*TypeConstraint=*/true)) + return ANK_Error; + return ANK_Success; + } } // Unable to classify the name, but maybe we can annotate a scope specifier. @@ -1810,7 +1834,7 @@ bool Parser::TryAnnotateTypeOrScopeToken() { /*EnteringContext=*/false, nullptr, /*IsTypename*/ true)) return true; - if (!SS.isSet()) { + if (SS.isEmpty()) { if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id) || Tok.is(tok::annot_decltype)) { // Attempt to recover by skipping the invalid 'typename' @@ -1983,7 +2007,7 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS, // template-id annotation in a context where we weren't allowed // to produce a type annotation token. Update the template-id // annotation token to a type annotation token now. - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(SS); return false; } } @@ -2005,10 +2029,7 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS, bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext) { assert(getLangOpts().CPlusPlus && "Call sites of this function should be guarded by checking for C++"); - assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) || - (Tok.is(tok::annot_template_id) && NextToken().is(tok::coloncolon)) || - Tok.is(tok::kw_decltype) || Tok.is(tok::kw___super)) && - "Cannot be a type or scope token!"); + assert(MightBeCXXScopeToken() && "Cannot be a type or scope token!"); CXXScopeSpec SS; if (ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext)) diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 639231c87232a..94d87974624e1 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -784,6 +784,15 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation TagKwLoc, return false; } +bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc, const char *&PrevSpec, + unsigned &DiagID, TemplateIdAnnotation *Rep, + const PrintingPolicy &Policy) { + assert(T == TST_auto || T == TST_decltype_auto); + ConstrainedAuto = true; + TemplateIdRep = Rep; + return SetTypeSpecType(T, Loc, PrevSpec, DiagID, Policy); +} + bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID, diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 2cd158a8b43c1..9cfce5a63b1df 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -52,6 +52,21 @@ SourceLocation Sema::getLocForEndOfToken(SourceLocation Loc, unsigned Offset) { ModuleLoader &Sema::getModuleLoader() const { return PP.getModuleLoader(); } +IdentifierInfo * +Sema::InventAbbreviatedTemplateParameterTypeName(IdentifierInfo *ParamName, + unsigned int Index) { + std::string InventedName; + llvm::raw_string_ostream OS(InventedName); + + if (!ParamName) + OS << "auto:" << Index + 1; + else + OS << ParamName->getName() << ":auto"; + + OS.flush(); + return &Context.Idents.get(OS.str()); +} + PrintingPolicy Sema::getPrintingPolicy(const ASTContext &Context, const Preprocessor &PP) { PrintingPolicy Policy = Context.getPrintingPolicy(); @@ -153,10 +168,10 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, TUKind(TUKind), NumSFINAEErrors(0), FullyCheckedComparisonCategories( static_cast<unsigned>(ComparisonCategoryType::Last) + 1), - AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false), - NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1), - CurrentInstantiationScope(nullptr), DisableTypoCorrection(false), - TyposCorrected(0), AnalysisWarnings(*this), + SatisfactionCache(Context), AccessCheckingSFINAE(false), + InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0), + ArgumentPackSubstitutionIndex(-1), CurrentInstantiationScope(nullptr), + DisableTypoCorrection(false), TyposCorrected(0), AnalysisWarnings(*this), ThreadSafetyDeclCache(nullptr), VarDataSharingAttributesStack(nullptr), CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) { TUScope = nullptr; @@ -379,6 +394,14 @@ Sema::~Sema() { if (isMultiplexExternalSource) delete ExternalSource; + // Delete cached satisfactions. + std::vector<ConstraintSatisfaction *> Satisfactions; + Satisfactions.reserve(Satisfactions.size()); + for (auto &Node : SatisfactionCache) + Satisfactions.push_back(&Node); + for (auto *Node : Satisfactions) + delete Node; + threadSafety::threadSafetyCleanup(ThreadSafetyDeclCache); // Destroys data sharing attributes stack for OpenMP @@ -1261,7 +1284,8 @@ DeclContext *Sema::getFunctionLevelDeclContext() { DeclContext *DC = CurContext; while (true) { - if (isa<BlockDecl>(DC) || isa<EnumDecl>(DC) || isa<CapturedDecl>(DC)) { + if (isa<BlockDecl>(DC) || isa<EnumDecl>(DC) || isa<CapturedDecl>(DC) || + isa<RequiresExprBodyDecl>(DC)) { DC = DC->getParent(); } else if (isa<CXXMethodDecl>(DC) && cast<CXXMethodDecl>(DC)->getOverloadedOperator() == OO_Call && diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 018ac2d7dc9d1..81601b09ce0d6 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -17,7 +17,10 @@ #include "clang/Sema/SemaDiagnostic.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/Template.h" -#include "clang/AST/ExprCXX.h" +#include "clang/Sema/Overload.h" +#include "clang/Sema/Initialization.h" +#include "clang/Sema/SemaInternal.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/OperatorPrecedence.h" #include "llvm/ADT/DenseMap.h" @@ -269,36 +272,56 @@ static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template, return false; } -bool Sema::CheckConstraintSatisfaction(TemplateDecl *Template, - ArrayRef<const Expr *> ConstraintExprs, - ArrayRef<TemplateArgument> TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction) { - return ::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, - TemplateArgs, TemplateIDRange, - Satisfaction); -} +bool Sema::CheckConstraintSatisfaction( + NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs, + ArrayRef<TemplateArgument> TemplateArgs, SourceRange TemplateIDRange, + ConstraintSatisfaction &OutSatisfaction) { + if (ConstraintExprs.empty()) { + OutSatisfaction.IsSatisfied = true; + return false; + } -bool -Sema::CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl* Part, - ArrayRef<const Expr *> ConstraintExprs, - ArrayRef<TemplateArgument> TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction) { - return ::CheckConstraintSatisfaction(*this, Part, ConstraintExprs, - TemplateArgs, TemplateIDRange, - Satisfaction); -} + llvm::FoldingSetNodeID ID; + void *InsertPos; + ConstraintSatisfaction *Satisfaction = nullptr; + if (LangOpts.ConceptSatisfactionCaching) { + ConstraintSatisfaction::Profile(ID, Context, Template, TemplateArgs); + Satisfaction = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos); + if (Satisfaction) { + OutSatisfaction = *Satisfaction; + return false; + } + Satisfaction = new ConstraintSatisfaction(Template, TemplateArgs); + } else { + Satisfaction = &OutSatisfaction; + } + bool Failed; + if (auto *T = dyn_cast<TemplateDecl>(Template)) + Failed = ::CheckConstraintSatisfaction(*this, T, ConstraintExprs, + TemplateArgs, TemplateIDRange, + *Satisfaction); + else if (auto *P = + dyn_cast<ClassTemplatePartialSpecializationDecl>(Template)) + Failed = ::CheckConstraintSatisfaction(*this, P, ConstraintExprs, + TemplateArgs, TemplateIDRange, + *Satisfaction); + else + Failed = ::CheckConstraintSatisfaction( + *this, cast<VarTemplatePartialSpecializationDecl>(Template), + ConstraintExprs, TemplateArgs, TemplateIDRange, *Satisfaction); + if (Failed) { + if (LangOpts.ConceptSatisfactionCaching) + delete Satisfaction; + return true; + } -bool -Sema::CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl* Partial, - ArrayRef<const Expr *> ConstraintExprs, - ArrayRef<TemplateArgument> TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction) { - return ::CheckConstraintSatisfaction(*this, Partial, ConstraintExprs, - TemplateArgs, TemplateIDRange, - Satisfaction); + if (LangOpts.ConceptSatisfactionCaching) { + // We cannot use InsertNode here because CheckConstraintSatisfaction might + // have invalidated it. + SatisfactionCache.InsertNode(Satisfaction); + OutSatisfaction = *Satisfaction; + } + return false; } bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr, @@ -336,6 +359,118 @@ bool Sema::EnsureTemplateArgumentListConstraints( return false; } +static void diagnoseUnsatisfiedRequirement(Sema &S, + concepts::ExprRequirement *Req, + bool First) { + assert(!Req->isSatisfied() + && "Diagnose() can only be used on an unsatisfied requirement"); + switch (Req->getSatisfactionStatus()) { + case concepts::ExprRequirement::SS_Dependent: + llvm_unreachable("Diagnosing a dependent requirement"); + break; + case concepts::ExprRequirement::SS_ExprSubstitutionFailure: { + auto *SubstDiag = Req->getExprSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_expr_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_expr_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + break; + } + case concepts::ExprRequirement::SS_NoexceptNotMet: + S.Diag(Req->getNoexceptLoc(), + diag::note_expr_requirement_noexcept_not_met) + << (int)First << Req->getExpr(); + break; + case concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure: { + auto *SubstDiag = + Req->getReturnTypeRequirement().getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_type_requirement_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_type_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + break; + } + case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: { + ConceptSpecializationExpr *ConstraintExpr = + Req->getReturnTypeRequirementSubstitutedConstraintExpr(); + if (ConstraintExpr->getTemplateArgsAsWritten()->NumTemplateArgs == 1) + // A simple case - expr type is the type being constrained and the concept + // was not provided arguments. + S.Diag(ConstraintExpr->getBeginLoc(), + diag::note_expr_requirement_constraints_not_satisfied_simple) + << (int)First << S.BuildDecltypeType(Req->getExpr(), + Req->getExpr()->getBeginLoc()) + << ConstraintExpr->getNamedConcept(); + else + S.Diag(ConstraintExpr->getBeginLoc(), + diag::note_expr_requirement_constraints_not_satisfied) + << (int)First << ConstraintExpr; + S.DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction()); + break; + } + case concepts::ExprRequirement::SS_Satisfied: + llvm_unreachable("We checked this above"); + } +} + +static void diagnoseUnsatisfiedRequirement(Sema &S, + concepts::TypeRequirement *Req, + bool First) { + assert(!Req->isSatisfied() + && "Diagnose() can only be used on an unsatisfied requirement"); + switch (Req->getSatisfactionStatus()) { + case concepts::TypeRequirement::SS_Dependent: + llvm_unreachable("Diagnosing a dependent requirement"); + return; + case concepts::TypeRequirement::SS_SubstitutionFailure: { + auto *SubstDiag = Req->getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_type_requirement_substitution_error) << (int)First + << SubstDiag->SubstitutedEntity << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_type_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + return; + } + default: + llvm_unreachable("Unknown satisfaction status"); + return; + } +} + +static void diagnoseUnsatisfiedRequirement(Sema &S, + concepts::NestedRequirement *Req, + bool First) { + if (Req->isSubstitutionFailure()) { + concepts::Requirement::SubstitutionDiagnostic *SubstDiag = + Req->getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_nested_requirement_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_nested_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + return; + } + S.DiagnoseUnsatisfiedConstraint(Req->getConstraintSatisfaction(), First); +} + + static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, Expr *SubstExpr, bool First = true) { @@ -412,6 +547,19 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, } S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction()); return; + } else if (auto *RE = dyn_cast<RequiresExpr>(SubstExpr)) { + for (concepts::Requirement *Req : RE->getRequirements()) + if (!Req->isDependent() && !Req->isSatisfied()) { + if (auto *E = dyn_cast<concepts::ExprRequirement>(Req)) + diagnoseUnsatisfiedRequirement(S, E, First); + else if (auto *T = dyn_cast<concepts::TypeRequirement>(Req)) + diagnoseUnsatisfiedRequirement(S, T, First); + else + diagnoseUnsatisfiedRequirement( + S, cast<concepts::NestedRequirement>(Req), First); + break; + } + return; } S.Diag(SubstExpr->getSourceRange().getBegin(), @@ -434,11 +582,11 @@ static void diagnoseUnsatisfiedConstraintExpr( Record.template get<Expr *>(), First); } -void Sema::DiagnoseUnsatisfiedConstraint( - const ConstraintSatisfaction& Satisfaction) { +void +Sema::DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction, + bool First) { assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); - bool First = true; for (auto &Pair : Satisfaction.Details) { diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First); First = false; @@ -446,10 +594,10 @@ void Sema::DiagnoseUnsatisfiedConstraint( } void Sema::DiagnoseUnsatisfiedConstraint( - const ASTConstraintSatisfaction &Satisfaction) { + const ASTConstraintSatisfaction &Satisfaction, + bool First) { assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); - bool First = true; for (auto &Pair : Satisfaction) { diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First); First = false; @@ -826,3 +974,67 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1, << AmbiguousAtomic2->getSourceRange(); return true; } + +concepts::ExprRequirement::ExprRequirement( + Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + ReturnTypeRequirement Req, SatisfactionStatus Status, + ConceptSpecializationExpr *SubstitutedConstraintExpr) : + Requirement(IsSimple ? RK_Simple : RK_Compound, Status == SS_Dependent, + Status == SS_Dependent && + (E->containsUnexpandedParameterPack() || + Req.containsUnexpandedParameterPack()), + Status == SS_Satisfied), Value(E), NoexceptLoc(NoexceptLoc), + TypeReq(Req), SubstitutedConstraintExpr(SubstitutedConstraintExpr), + Status(Status) { + assert((!IsSimple || (Req.isEmpty() && NoexceptLoc.isInvalid())) && + "Simple requirement must not have a return type requirement or a " + "noexcept specification"); + assert((Status > SS_TypeRequirementSubstitutionFailure && Req.isTypeConstraint()) == + (SubstitutedConstraintExpr != nullptr)); +} + +concepts::ExprRequirement::ExprRequirement( + SubstitutionDiagnostic *ExprSubstDiag, bool IsSimple, + SourceLocation NoexceptLoc, ReturnTypeRequirement Req) : + Requirement(IsSimple ? RK_Simple : RK_Compound, Req.isDependent(), + Req.containsUnexpandedParameterPack(), /*IsSatisfied=*/false), + Value(ExprSubstDiag), NoexceptLoc(NoexceptLoc), TypeReq(Req), + Status(SS_ExprSubstitutionFailure) { + assert((!IsSimple || (Req.isEmpty() && NoexceptLoc.isInvalid())) && + "Simple requirement must not have a return type requirement or a " + "noexcept specification"); +} + +concepts::ExprRequirement::ReturnTypeRequirement:: +ReturnTypeRequirement(TemplateParameterList *TPL) : + TypeConstraintInfo(TPL, 0) { + assert(TPL->size() == 1); + const TypeConstraint *TC = + cast<TemplateTypeParmDecl>(TPL->getParam(0))->getTypeConstraint(); + assert(TC && + "TPL must have a template type parameter with a type constraint"); + auto *Constraint = + cast_or_null<ConceptSpecializationExpr>( + TC->getImmediatelyDeclaredConstraint()); + bool Dependent = false; + if (Constraint->getTemplateArgsAsWritten()) { + for (auto &ArgLoc : + Constraint->getTemplateArgsAsWritten()->arguments().drop_front(1)) { + if (ArgLoc.getArgument().isDependent()) { + Dependent = true; + break; + } + } + } + TypeConstraintInfo.setInt(Dependent ? 1 : 0); +} + +concepts::TypeRequirement::TypeRequirement(TypeSourceInfo *T) : + Requirement(RK_Type, T->getType()->isDependentType(), + T->getType()->containsUnexpandedParameterPack(), + // We reach this ctor with either dependent types (in which + // IsSatisfied doesn't matter) or with non-dependent type in + // which the existence of the type indicates satisfaction. + /*IsSatisfied=*/true + ), Value(T), + Status(T->getType()->isDependentType() ? SS_Dependent : SS_Satisfied) {} diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 507e4a6cd4365..0bf4903365370 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "TreeTransform.h" #include "TypeLocBuilder.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -1153,6 +1154,10 @@ Corrected: return ParsedType::make(T); } + if (isa<ConceptDecl>(FirstDecl)) + return NameClassification::Concept( + TemplateName(cast<TemplateDecl>(FirstDecl))); + // We can have a type template here if we're classifying a template argument. if (isa<TemplateDecl>(FirstDecl) && !isa<FunctionTemplateDecl>(FirstDecl) && !isa<VarTemplateDecl>(FirstDecl)) @@ -6468,6 +6473,8 @@ static bool shouldConsiderLinkage(const VarDecl *VD) { return true; if (DC->isRecord()) return false; + if (isa<RequiresExprBodyDecl>(DC)) + return false; llvm_unreachable("Unexpected context"); } @@ -8654,11 +8661,21 @@ static Scope *getTagInjectionScope(Scope *S, const LangOptions &LangOpts) { NamedDecl* Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, LookupResult &Previous, - MultiTemplateParamsArg TemplateParamLists, + MultiTemplateParamsArg TemplateParamListsRef, bool &AddToScope) { QualType R = TInfo->getType(); assert(R->isFunctionType()); + SmallVector<TemplateParameterList *, 4> TemplateParamLists; + for (TemplateParameterList *TPL : TemplateParamListsRef) + TemplateParamLists.push_back(TPL); + if (TemplateParameterList *Invented = D.getInventedTemplateParameterList()) { + if (!TemplateParamLists.empty() && + Invented->getDepth() == TemplateParamLists.back()->getDepth()) + TemplateParamLists.back() = Invented; + else + TemplateParamLists.push_back(Invented); + } // TODO: consider using NameInfo for diagnostic. DeclarationNameInfo NameInfo = GetNameForDeclarator(D); @@ -8738,15 +8755,16 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // Match up the template parameter lists with the scope specifier, then // determine whether we have a template or a template specialization. bool Invalid = false; - if (TemplateParameterList *TemplateParams = - MatchTemplateParametersToScopeSpecifier( - D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(), - D.getCXXScopeSpec(), - D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId - ? D.getName().TemplateId - : nullptr, - TemplateParamLists, isFriend, isMemberSpecialization, - Invalid)) { + TemplateParameterList *TemplateParams = + MatchTemplateParametersToScopeSpecifier( + D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(), + D.getCXXScopeSpec(), + D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId + ? D.getName().TemplateId + : nullptr, + TemplateParamLists, isFriend, isMemberSpecialization, + Invalid); + if (TemplateParams) { if (TemplateParams->size() > 0) { // This is a function template @@ -8779,7 +8797,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // For source fidelity, store the other template param lists. if (TemplateParamLists.size() > 1) { NewFD->setTemplateParameterListsInfo(Context, - TemplateParamLists.drop_back(1)); + ArrayRef<TemplateParameterList *>(TemplateParamLists) + .drop_back(1)); } } else { // This is a function template specialization. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 5c51b0f9b8cb7..849bc09063b37 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4924,9 +4924,9 @@ static void handlePatchableFunctionEntryAttr(Sema &S, Decl *D, Expr *Arg = AL.getArgAsExpr(1); if (!checkUInt32Argument(S, AL, Arg, Offset, 1, true)) return; - if (Offset) { + if (Count < Offset) { S.Diag(getAttrLoc(AL), diag::err_attribute_argument_out_of_range) - << &AL << 0 << 0 << Arg->getBeginLoc(); + << &AL << 0 << Count << Arg->getBeginLoc(); return; } } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 9916d3be77e10..9fa5691983a17 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17386,3 +17386,50 @@ MSPropertyDecl *Sema::HandleMSProperty(Scope *S, RecordDecl *Record, return NewPD; } + +void Sema::ActOnStartFunctionDeclarationDeclarator( + Declarator &Declarator, unsigned TemplateParameterDepth) { + auto &Info = InventedParameterInfos.emplace_back(); + TemplateParameterList *ExplicitParams = nullptr; + ArrayRef<TemplateParameterList *> ExplicitLists = + Declarator.getTemplateParameterLists(); + if (!ExplicitLists.empty()) { + bool IsMemberSpecialization, IsInvalid; + ExplicitParams = MatchTemplateParametersToScopeSpecifier( + Declarator.getBeginLoc(), Declarator.getIdentifierLoc(), + Declarator.getCXXScopeSpec(), /*TemplateId=*/nullptr, + ExplicitLists, /*IsFriend=*/false, IsMemberSpecialization, IsInvalid, + /*SuppressDiagnostic=*/true); + } + if (ExplicitParams) { + Info.AutoTemplateParameterDepth = ExplicitParams->getDepth(); + for (NamedDecl *Param : *ExplicitParams) + Info.TemplateParams.push_back(Param); + Info.NumExplicitTemplateParams = ExplicitParams->size(); + } else { + Info.AutoTemplateParameterDepth = TemplateParameterDepth; + Info.NumExplicitTemplateParams = 0; + } +} + +void Sema::ActOnFinishFunctionDeclarationDeclarator(Declarator &Declarator) { + auto &FSI = InventedParameterInfos.back(); + if (FSI.TemplateParams.size() > FSI.NumExplicitTemplateParams) { + if (FSI.NumExplicitTemplateParams != 0) { + TemplateParameterList *ExplicitParams = + Declarator.getTemplateParameterLists().back(); + Declarator.setInventedTemplateParameterList( + TemplateParameterList::Create( + Context, ExplicitParams->getTemplateLoc(), + ExplicitParams->getLAngleLoc(), FSI.TemplateParams, + ExplicitParams->getRAngleLoc(), + ExplicitParams->getRequiresClause())); + } else { + Declarator.setInventedTemplateParameterList( + TemplateParameterList::Create( + Context, SourceLocation(), SourceLocation(), FSI.TemplateParams, + SourceLocation(), /*RequiresClause=*/nullptr)); + } + } + InventedParameterInfos.pop_back(); +} diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 5aedbe7644e4b..193eaa3e01f93 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1386,6 +1386,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::StringLiteralClass: case Expr::SourceLocExprClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: // These expressions can never throw. return CT_Cannot; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 5f4071924d3f1..ea4b93ee6a5a4 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -350,6 +350,17 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs, } } + if (isa<ParmVarDecl>(D) && isa<RequiresExprBodyDecl>(D->getDeclContext()) && + !isUnevaluatedContext()) { + // C++ [expr.prim.req.nested] p3 + // A local parameter shall only appear as an unevaluated operand + // (Clause 8) within the constraint-expression. + Diag(Loc, diag::err_requires_expr_parameter_referenced_in_evaluated_context) + << D; + Diag(D->getLocation(), diag::note_entity_declared_at) << D; + return true; + } + return false; } @@ -1904,7 +1915,7 @@ Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK, bool RefersToCapturedVariable = isa<VarDecl>(D) && NeedToCaptureVariable(cast<VarDecl>(D), NameInfo.getLoc()); - + DeclRefExpr *E = DeclRefExpr::Create( Context, NNS, TemplateKWLoc, D, RefersToCapturedVariable, NameInfo, Ty, VK, FoundD, TemplateArgs, getNonOdrUseReasonInCurrentContext(D)); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index a73e6906fceb9..192c237b6c1ce 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -11,6 +11,7 @@ /// //===----------------------------------------------------------------------===// +#include "clang/Sema/Template.h" #include "clang/Sema/SemaInternal.h" #include "TreeTransform.h" #include "TypeLocBuilder.h" @@ -7317,7 +7318,7 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base, ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), TemplateId->NumArgs); TypeResult T = ActOnTemplateIdType(S, - TemplateId->SS, + SS, TemplateId->TemplateKWLoc, TemplateId->Template, TemplateId->Name, @@ -7370,7 +7371,7 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base, ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), TemplateId->NumArgs); TypeResult T = ActOnTemplateIdType(S, - TemplateId->SS, + SS, TemplateId->TemplateKWLoc, TemplateId->Template, TemplateId->Name, @@ -8331,3 +8332,215 @@ Sema::CheckMicrosoftIfExistsSymbol(Scope *S, SourceLocation KeywordLoc, return CheckMicrosoftIfExistsSymbol(S, SS, TargetNameInfo); } + +concepts::Requirement *Sema::ActOnSimpleRequirement(Expr *E) { + return BuildExprRequirement(E, /*IsSimple=*/true, + /*NoexceptLoc=*/SourceLocation(), + /*ReturnTypeRequirement=*/{}); +} + +concepts::Requirement * +Sema::ActOnTypeRequirement(SourceLocation TypenameKWLoc, CXXScopeSpec &SS, + SourceLocation NameLoc, IdentifierInfo *TypeName, + TemplateIdAnnotation *TemplateId) { + assert(((!TypeName && TemplateId) || (TypeName && !TemplateId)) && + "Exactly one of TypeName and TemplateId must be specified."); + TypeSourceInfo *TSI = nullptr; + if (TypeName) { + QualType T = CheckTypenameType(ETK_Typename, TypenameKWLoc, + SS.getWithLocInContext(Context), *TypeName, + NameLoc, &TSI, /*DeducedTypeContext=*/false); + if (T.isNull()) + return nullptr; + } else { + ASTTemplateArgsPtr ArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + TypeResult T = ActOnTypenameType(CurScope, TypenameKWLoc, SS, + TemplateId->TemplateKWLoc, + TemplateId->Template, TemplateId->Name, + TemplateId->TemplateNameLoc, + TemplateId->LAngleLoc, ArgsPtr, + TemplateId->RAngleLoc); + if (T.isInvalid()) + return nullptr; + if (GetTypeFromParser(T.get(), &TSI).isNull()) + return nullptr; + } + return BuildTypeRequirement(TSI); +} + +concepts::Requirement * +Sema::ActOnCompoundRequirement(Expr *E, SourceLocation NoexceptLoc) { + return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, + /*ReturnTypeRequirement=*/{}); +} + +concepts::Requirement * +Sema::ActOnCompoundRequirement( + Expr *E, SourceLocation NoexceptLoc, CXXScopeSpec &SS, + TemplateIdAnnotation *TypeConstraint, unsigned Depth) { + // C++2a [expr.prim.req.compound] p1.3.3 + // [..] the expression is deduced against an invented function template + // F [...] F is a void function template with a single type template + // parameter T declared with the constrained-parameter. Form a new + // cv-qualifier-seq cv by taking the union of const and volatile specifiers + // around the constrained-parameter. F has a single parameter whose + // type-specifier is cv T followed by the abstract-declarator. [...] + // + // The cv part is done in the calling function - we get the concept with + // arguments and the abstract declarator with the correct CV qualification and + // have to synthesize T and the single parameter of F. + auto &II = Context.Idents.get("expr-type"); + auto *TParam = TemplateTypeParmDecl::Create(Context, CurContext, + SourceLocation(), + SourceLocation(), Depth, + /*Index=*/0, &II, + /*Typename=*/true, + /*ParameterPack=*/false, + /*HasTypeConstraint=*/true); + + if (ActOnTypeConstraint(SS, TypeConstraint, TParam, + /*EllpsisLoc=*/SourceLocation())) + // Just produce a requirement with no type requirements. + return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, {}); + + auto *TPL = TemplateParameterList::Create(Context, SourceLocation(), + SourceLocation(), + ArrayRef<NamedDecl *>(TParam), + SourceLocation(), + /*RequiresClause=*/nullptr); + return BuildExprRequirement( + E, /*IsSimple=*/false, NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement(TPL)); +} + +concepts::ExprRequirement * +Sema::BuildExprRequirement( + Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) { + auto Status = concepts::ExprRequirement::SS_Satisfied; + ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr; + if (E->isInstantiationDependent() || ReturnTypeRequirement.isDependent()) + Status = concepts::ExprRequirement::SS_Dependent; + else if (NoexceptLoc.isValid() && canThrow(E) == CanThrowResult::CT_Can) + Status = concepts::ExprRequirement::SS_NoexceptNotMet; + else if (ReturnTypeRequirement.isSubstitutionFailure()) + Status = concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure; + else if (ReturnTypeRequirement.isTypeConstraint()) { + // C++2a [expr.prim.req]p1.3.3 + // The immediately-declared constraint ([temp]) of decltype((E)) shall + // be satisfied. + TemplateParameterList *TPL = + ReturnTypeRequirement.getTypeConstraintTemplateParameterList(); + QualType MatchedType = + BuildDecltypeType(E, E->getBeginLoc()).getCanonicalType(); + llvm::SmallVector<TemplateArgument, 1> Args; + Args.push_back(TemplateArgument(MatchedType)); + TemplateArgumentList TAL(TemplateArgumentList::OnStack, Args); + MultiLevelTemplateArgumentList MLTAL(TAL); + for (unsigned I = 0; I < TPL->getDepth(); ++I) + MLTAL.addOuterRetainedLevel(); + Expr *IDC = + cast<TemplateTypeParmDecl>(TPL->getParam(0))->getTypeConstraint() + ->getImmediatelyDeclaredConstraint(); + ExprResult Constraint = SubstExpr(IDC, MLTAL); + assert(!Constraint.isInvalid() && + "Substitution cannot fail as it is simply putting a type template " + "argument into a concept specialization expression's parameter."); + + SubstitutedConstraintExpr = + cast<ConceptSpecializationExpr>(Constraint.get()); + if (!SubstitutedConstraintExpr->isSatisfied()) + Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied; + } + return new (Context) concepts::ExprRequirement(E, IsSimple, NoexceptLoc, + ReturnTypeRequirement, Status, + SubstitutedConstraintExpr); +} + +concepts::ExprRequirement * +Sema::BuildExprRequirement( + concepts::Requirement::SubstitutionDiagnostic *ExprSubstitutionDiagnostic, + bool IsSimple, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) { + return new (Context) concepts::ExprRequirement(ExprSubstitutionDiagnostic, + IsSimple, NoexceptLoc, + ReturnTypeRequirement); +} + +concepts::TypeRequirement * +Sema::BuildTypeRequirement(TypeSourceInfo *Type) { + return new (Context) concepts::TypeRequirement(Type); +} + +concepts::TypeRequirement * +Sema::BuildTypeRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + return new (Context) concepts::TypeRequirement(SubstDiag); +} + +concepts::Requirement *Sema::ActOnNestedRequirement(Expr *Constraint) { + return BuildNestedRequirement(Constraint); +} + +concepts::NestedRequirement * +Sema::BuildNestedRequirement(Expr *Constraint) { + ConstraintSatisfaction Satisfaction; + if (!Constraint->isInstantiationDependent() && + CheckConstraintSatisfaction(Constraint, Satisfaction)) + return nullptr; + return new (Context) concepts::NestedRequirement(Context, Constraint, + Satisfaction); +} + +concepts::NestedRequirement * +Sema::BuildNestedRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + return new (Context) concepts::NestedRequirement(SubstDiag); +} + +RequiresExprBodyDecl * +Sema::ActOnStartRequiresExpr(SourceLocation RequiresKWLoc, + ArrayRef<ParmVarDecl *> LocalParameters, + Scope *BodyScope) { + assert(BodyScope); + + RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create(Context, CurContext, + RequiresKWLoc); + + PushDeclContext(BodyScope, Body); + + for (ParmVarDecl *Param : LocalParameters) { + if (Param->hasDefaultArg()) + // C++2a [expr.prim.req] p4 + // [...] A local parameter of a requires-expression shall not have a + // default argument. [...] + Diag(Param->getDefaultArgRange().getBegin(), + diag::err_requires_expr_local_parameter_default_argument); + // Ignore default argument and move on + + Param->setDeclContext(Body); + // If this has an identifier, add it to the scope stack. + if (Param->getIdentifier()) { + CheckShadow(BodyScope, Param); + PushOnScopeChains(Param, BodyScope); + } + } + return Body; +} + +void Sema::ActOnFinishRequiresExpr() { + assert(CurContext && "DeclContext imbalance!"); + CurContext = CurContext->getLexicalParent(); + assert(CurContext && "Popped translation unit!"); +} + +ExprResult +Sema::ActOnRequiresExpr(SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef<ParmVarDecl *> LocalParameters, + ArrayRef<concepts::Requirement *> Requirements, + SourceLocation ClosingBraceLoc) { + return RequiresExpr::Create(Context, RequiresKWLoc, Body, LocalParameters, + Requirements, ClosingBraceLoc); +} diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index c2d14a44f53d4..ae89b146c409b 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -791,7 +791,8 @@ QualType Sema::buildLambdaInitCaptureInitialization( // deduce against. QualType DeductType = Context.getAutoDeductType(); TypeLocBuilder TLB; - TLB.pushTypeSpec(DeductType).setNameLoc(Loc); + AutoTypeLoc TL = TLB.push<AutoTypeLoc>(DeductType); + TL.setNameLoc(Loc); if (ByRef) { DeductType = BuildReferenceType(DeductType, true, Loc, Id); assert(!DeductType.isNull() && "can't build reference to auto"); diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 0ed51de0cc131..8d96404a5c27d 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1575,7 +1575,9 @@ llvm::DenseSet<Module*> &Sema::getLookupModules() { unsigned N = CodeSynthesisContexts.size(); for (unsigned I = CodeSynthesisContextLookupModules.size(); I != N; ++I) { - Module *M = getDefiningModule(*this, CodeSynthesisContexts[I].Entity); + Module *M = CodeSynthesisContexts[I].Entity ? + getDefiningModule(*this, CodeSynthesisContexts[I].Entity) : + nullptr; if (M && !LookupModulesCache.insert(M).second) M = nullptr; CodeSynthesisContextLookupModules.push_back(M); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index d6c3af9e84c80..ff64810062801 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2838,6 +2838,9 @@ static void DiagnoseForRangeConstVariableCopies(Sema &SemaRef, /// Suggest "const foo &x" to prevent the copy. static void DiagnoseForRangeVariableCopies(Sema &SemaRef, const CXXForRangeStmt *ForStmt) { + if (SemaRef.inTemplateInstantiation()) + return; + if (SemaRef.Diags.isIgnored(diag::warn_for_range_const_reference_copy, ForStmt->getBeginLoc()) && SemaRef.Diags.isIgnored(diag::warn_for_range_variable_always_copy, @@ -2860,6 +2863,9 @@ static void DiagnoseForRangeVariableCopies(Sema &SemaRef, if (!InitExpr) return; + if (InitExpr->getExprLoc().isMacroID()) + return; + if (VariableType->isReferenceType()) { DiagnoseForRangeReferenceVariableCopies(SemaRef, VD, ForStmt->getRangeInit()->getType()); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 1184446796eba..f961244da0726 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1050,7 +1050,8 @@ makeTemplateArgumentListInfo(Sema &S, TemplateIdAnnotation &TemplateId) { return TemplateArgs; } -bool Sema::ActOnTypeConstraint(TemplateIdAnnotation *TypeConstr, +bool Sema::ActOnTypeConstraint(const CXXScopeSpec &SS, + TemplateIdAnnotation *TypeConstr, TemplateTypeParmDecl *ConstrainedParameter, SourceLocation EllipsisLoc) { ConceptDecl *CD = @@ -1080,14 +1081,57 @@ bool Sema::ActOnTypeConstraint(TemplateIdAnnotation *TypeConstr, makeTemplateArgumentListInfo(*this, *TypeConstr); } return AttachTypeConstraint( - TypeConstr->SS.isSet() ? TypeConstr->SS.getWithLocInContext(Context) : - NestedNameSpecifierLoc(), + SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc(), DeclarationNameInfo(DeclarationName(TypeConstr->Name), TypeConstr->TemplateNameLoc), CD, TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr, ConstrainedParameter, EllipsisLoc); } +template<typename ArgumentLocAppender> +static ExprResult formImmediatelyDeclaredConstraint( + Sema &S, NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo, + ConceptDecl *NamedConcept, SourceLocation LAngleLoc, + SourceLocation RAngleLoc, QualType ConstrainedType, + SourceLocation ParamNameLoc, ArgumentLocAppender Appender, + SourceLocation EllipsisLoc) { + + TemplateArgumentListInfo ConstraintArgs; + ConstraintArgs.addArgument( + S.getTrivialTemplateArgumentLoc(TemplateArgument(ConstrainedType), + /*NTTPType=*/QualType(), ParamNameLoc)); + + ConstraintArgs.setRAngleLoc(RAngleLoc); + ConstraintArgs.setLAngleLoc(LAngleLoc); + Appender(ConstraintArgs); + + // C++2a [temp.param]p4: + // [...] This constraint-expression E is called the immediately-declared + // constraint of T. [...] + CXXScopeSpec SS; + SS.Adopt(NS); + ExprResult ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId( + SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo, + /*FoundDecl=*/NamedConcept, NamedConcept, &ConstraintArgs); + if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid()) + return ImmediatelyDeclaredConstraint; + + // C++2a [temp.param]p4: + // [...] If T is not a pack, then E is E', otherwise E is (E' && ...). + // + // We have the following case: + // + // template<typename T> concept C1 = true; + // template<C1... T> struct s1; + // + // The constraint: (C1<T> && ...) + return S.BuildCXXFoldExpr(/*LParenLoc=*/SourceLocation(), + ImmediatelyDeclaredConstraint.get(), BO_LAnd, + EllipsisLoc, /*RHS=*/nullptr, + /*RParenLoc=*/SourceLocation(), + /*NumExpansions=*/None); +} + /// Attach a type-constraint to a template parameter. /// \returns true if an error occured. This can happen if the /// immediately-declared constraint could not be formed (e.g. incorrect number @@ -1106,51 +1150,21 @@ bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS, *TemplateArgs) : nullptr; QualType ParamAsArgument(ConstrainedParameter->getTypeForDecl(), 0); - TemplateArgumentListInfo ConstraintArgs; - ConstraintArgs.addArgument( - TemplateArgumentLoc( - TemplateArgument(ParamAsArgument), - TemplateArgumentLocInfo( - Context.getTrivialTypeSourceInfo(ParamAsArgument, - ConstrainedParameter->getLocation())))); - if (TemplateArgs) { - ConstraintArgs.setRAngleLoc(TemplateArgs->getRAngleLoc()); - ConstraintArgs.setLAngleLoc(TemplateArgs->getLAngleLoc()); - for (const TemplateArgumentLoc &ArgLoc : TemplateArgs->arguments()) - ConstraintArgs.addArgument(ArgLoc); - } - // C++2a [temp.param]p4: - // [...] This constraint-expression E is called the immediately-declared - // constraint of T. [...] - CXXScopeSpec SS; - SS.Adopt(NS); - ExprResult ImmediatelyDeclaredConstraint = CheckConceptTemplateId(SS, - /*TemplateKWLoc=*/SourceLocation(), NameInfo, /*FoundDecl=*/NamedConcept, - NamedConcept, &ConstraintArgs); + ExprResult ImmediatelyDeclaredConstraint = + formImmediatelyDeclaredConstraint( + *this, NS, NameInfo, NamedConcept, + TemplateArgs ? TemplateArgs->getLAngleLoc() : SourceLocation(), + TemplateArgs ? TemplateArgs->getRAngleLoc() : SourceLocation(), + ParamAsArgument, ConstrainedParameter->getLocation(), + [&] (TemplateArgumentListInfo &ConstraintArgs) { + if (TemplateArgs) + for (const auto &ArgLoc : TemplateArgs->arguments()) + ConstraintArgs.addArgument(ArgLoc); + }, EllipsisLoc); if (ImmediatelyDeclaredConstraint.isInvalid()) return true; - if (ConstrainedParameter->isParameterPack()) { - // C++2a [temp.param]p4: - // [...] If T is not a pack, then E is E', otherwise E is (E' && ...). - // - // We have the following case: - // - // template<typename T> concept C1 = true; - // template<C1... T> struct s1; - // - // The constraint: (C1<T> && ...) - ImmediatelyDeclaredConstraint = - BuildCXXFoldExpr(/*LParenLoc=*/SourceLocation(), - ImmediatelyDeclaredConstraint.get(), BO_LAnd, - EllipsisLoc, /*RHS=*/nullptr, - /*RParenLoc=*/SourceLocation(), - /*NumExpansions=*/None).get(); - if (ImmediatelyDeclaredConstraint.isInvalid()) - return true; - } - ConstrainedParameter->setTypeConstraint(NS, NameInfo, /*FoundDecl=*/NamedConcept, NamedConcept, ArgsAsWritten, @@ -1158,6 +1172,38 @@ bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS, return false; } +bool Sema::AttachTypeConstraint(AutoTypeLoc TL, NonTypeTemplateParmDecl *NTTP, + SourceLocation EllipsisLoc) { + if (NTTP->getType() != TL.getType() || + TL.getAutoKeyword() != AutoTypeKeyword::Auto) { + Diag(NTTP->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), + diag::err_unsupported_placeholder_constraint) + << NTTP->getTypeSourceInfo()->getTypeLoc().getSourceRange(); + return true; + } + // FIXME: Concepts: This should be the type of the placeholder, but this is + // unclear in the wording right now. + DeclRefExpr *Ref = BuildDeclRefExpr(NTTP, NTTP->getType(), VK_RValue, + NTTP->getLocation()); + if (!Ref) + return true; + ExprResult ImmediatelyDeclaredConstraint = + formImmediatelyDeclaredConstraint( + *this, TL.getNestedNameSpecifierLoc(), TL.getConceptNameInfo(), + TL.getNamedConcept(), TL.getLAngleLoc(), TL.getRAngleLoc(), + BuildDecltypeType(Ref, NTTP->getLocation()), NTTP->getLocation(), + [&] (TemplateArgumentListInfo &ConstraintArgs) { + for (unsigned I = 0, C = TL.getNumArgs(); I != C; ++I) + ConstraintArgs.addArgument(TL.getArgLoc(I)); + }, EllipsisLoc); + if (ImmediatelyDeclaredConstraint.isInvalid() || + !ImmediatelyDeclaredConstraint.isUsable()) + return true; + + NTTP->setPlaceholderTypeConstraint(ImmediatelyDeclaredConstraint.get()); + return false; +} + /// Check that the type of a non-type template parameter is /// well-formed. /// @@ -1319,6 +1365,11 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, TInfo); Param->setAccess(AS_public); + if (AutoTypeLoc TL = TInfo->getTypeLoc().getContainedAutoTypeLoc()) + if (TL.isConstrained()) + if (AttachTypeConstraint(TL, Param, D.getEllipsisLoc())) + Invalid = true; + if (Invalid) Param->setInvalidDecl(); @@ -2762,7 +2813,7 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( SourceLocation DeclStartLoc, SourceLocation DeclLoc, const CXXScopeSpec &SS, TemplateIdAnnotation *TemplateId, ArrayRef<TemplateParameterList *> ParamLists, bool IsFriend, - bool &IsMemberSpecialization, bool &Invalid) { + bool &IsMemberSpecialization, bool &Invalid, bool SuppressDiagnostic) { IsMemberSpecialization = false; Invalid = false; @@ -2870,8 +2921,9 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( auto CheckExplicitSpecialization = [&](SourceRange Range, bool Recovery) { if (SawNonEmptyTemplateParameterList) { - Diag(DeclLoc, diag::err_specialize_member_of_template) - << !Recovery << Range; + if (!SuppressDiagnostic) + Diag(DeclLoc, diag::err_specialize_member_of_template) + << !Recovery << Range; Invalid = true; IsMemberSpecialization = false; return true; @@ -2892,9 +2944,10 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( else ExpectedTemplateLoc = DeclStartLoc; - Diag(DeclLoc, diag::err_template_spec_needs_header) - << Range - << FixItHint::CreateInsertion(ExpectedTemplateLoc, "template<> "); + if (!SuppressDiagnostic) + Diag(DeclLoc, diag::err_template_spec_needs_header) + << Range + << FixItHint::CreateInsertion(ExpectedTemplateLoc, "template<> "); return false; }; @@ -2984,12 +3037,13 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( if (ParamIdx < ParamLists.size()) { if (ParamLists[ParamIdx]->size() > 0) { // The header has template parameters when it shouldn't. Complain. - Diag(ParamLists[ParamIdx]->getTemplateLoc(), - diag::err_template_param_list_matches_nontemplate) - << T - << SourceRange(ParamLists[ParamIdx]->getLAngleLoc(), - ParamLists[ParamIdx]->getRAngleLoc()) - << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); + if (!SuppressDiagnostic) + Diag(ParamLists[ParamIdx]->getTemplateLoc(), + diag::err_template_param_list_matches_nontemplate) + << T + << SourceRange(ParamLists[ParamIdx]->getLAngleLoc(), + ParamLists[ParamIdx]->getRAngleLoc()) + << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); Invalid = true; return nullptr; } @@ -3025,7 +3079,7 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( if (ExpectedTemplateParams && !TemplateParameterListsAreEqual(ParamLists[ParamIdx], ExpectedTemplateParams, - true, TPL_TemplateMatch)) + !SuppressDiagnostic, TPL_TemplateMatch)) Invalid = true; if (!Invalid && @@ -3037,9 +3091,10 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( continue; } - Diag(DeclLoc, diag::err_template_spec_needs_template_parameters) - << T - << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); + if (!SuppressDiagnostic) + Diag(DeclLoc, diag::err_template_spec_needs_template_parameters) + << T + << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); Invalid = true; continue; } @@ -3075,16 +3130,18 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( AllExplicitSpecHeaders = false; } - Diag(ParamLists[ParamIdx]->getTemplateLoc(), - AllExplicitSpecHeaders ? diag::warn_template_spec_extra_headers - : diag::err_template_spec_extra_headers) - << SourceRange(ParamLists[ParamIdx]->getTemplateLoc(), - ParamLists[ParamLists.size() - 2]->getRAngleLoc()); + if (!SuppressDiagnostic) + Diag(ParamLists[ParamIdx]->getTemplateLoc(), + AllExplicitSpecHeaders ? diag::warn_template_spec_extra_headers + : diag::err_template_spec_extra_headers) + << SourceRange(ParamLists[ParamIdx]->getTemplateLoc(), + ParamLists[ParamLists.size() - 2]->getRAngleLoc()); // If there was a specialization somewhere, such that 'template<>' is // not required, and there were any 'template<>' headers, note where the // specialization occurred. - if (ExplicitSpecLoc.isValid() && HasAnyExplicitSpecHeader) + if (ExplicitSpecLoc.isValid() && HasAnyExplicitSpecHeader && + !SuppressDiagnostic) Diag(ExplicitSpecLoc, diag::note_explicit_template_spec_does_not_need_header) << NestedTypes.back(); @@ -4044,7 +4101,7 @@ DeclResult Sema::ActOnVarTemplateSpecialization( if (isSameAsPrimaryTemplate(VarTemplate->getTemplateParameters(), Converted) && - (!Context.getLangOpts().ConceptsTS || + (!Context.getLangOpts().CPlusPlus2a || !TemplateParams->hasAssociatedConstraints())) { // C++ [temp.class.spec]p9b3: // @@ -6530,7 +6587,12 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, DeductionArg = PE->getPattern(); if (DeduceAutoType( Context.getTrivialTypeSourceInfo(ParamType, Param->getLocation()), - DeductionArg, ParamType, Depth) == DAR_Failed) { + DeductionArg, ParamType, Depth, + // We do not check constraints right now because the + // immediately-declared constraint of the auto type is also an + // associated constraint, and will be checked along with the other + // associated constraints after checking the template argument list. + /*IgnoreConstraints=*/true) == DAR_Failed) { Diag(Arg->getExprLoc(), diag::err_non_type_template_parm_type_deduction_failure) << Param->getDeclName() << Param->getType() << Arg->getType() @@ -7102,6 +7164,11 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, // [temp.constr.order]. SmallVector<const Expr *, 3> ParamsAC, TemplateAC; Params->getAssociatedConstraints(ParamsAC); + // C++2a[temp.arg.template]p3 + // [...] In this comparison, if P is unconstrained, the constraints on A + // are not considered. + if (ParamsAC.empty()) + return false; Template->getAssociatedConstraints(TemplateAC); bool IsParamAtLeastAsConstrained; if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, @@ -7872,13 +7939,11 @@ bool Sema::CheckTemplatePartialSpecializationArgs( DeclResult Sema::ActOnClassTemplateSpecialization( Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, - SourceLocation ModulePrivateLoc, TemplateIdAnnotation &TemplateId, - const ParsedAttributesView &Attr, + SourceLocation ModulePrivateLoc, CXXScopeSpec &SS, + TemplateIdAnnotation &TemplateId, const ParsedAttributesView &Attr, MultiTemplateParamsArg TemplateParameterLists, SkipBodyInfo *SkipBody) { assert(TUK != TUK_Reference && "References are not specializations"); - CXXScopeSpec &SS = TemplateId.SS; - // NOTE: KWLoc is the location of the tag keyword. This will instead // store the location of the outermost template keyword in the declaration. SourceLocation TemplateKWLoc = TemplateParameterLists.size() > 0 @@ -8048,7 +8113,7 @@ DeclResult Sema::ActOnClassTemplateSpecialization( if (Context.hasSameType(CanonType, ClassTemplate->getInjectedClassNameSpecialization()) && - (!Context.getLangOpts().ConceptsTS || + (!Context.getLangOpts().CPlusPlus2a || !TemplateParams->hasAssociatedConstraints())) { // C++ [temp.class.spec]p9b3: // @@ -10012,24 +10077,12 @@ Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc, << FixItHint::CreateRemoval(TypenameLoc); NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context); + TypeSourceInfo *TSI = nullptr; QualType T = CheckTypenameType(TypenameLoc.isValid()? ETK_Typename : ETK_None, - TypenameLoc, QualifierLoc, II, IdLoc); + TypenameLoc, QualifierLoc, II, IdLoc, &TSI, + /*DeducedTSTContext=*/true); if (T.isNull()) return true; - - TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T); - if (isa<DependentNameType>(T)) { - DependentNameTypeLoc TL = TSI->getTypeLoc().castAs<DependentNameTypeLoc>(); - TL.setElaboratedKeywordLoc(TypenameLoc); - TL.setQualifierLoc(QualifierLoc); - TL.setNameLoc(IdLoc); - } else { - ElaboratedTypeLoc TL = TSI->getTypeLoc().castAs<ElaboratedTypeLoc>(); - TL.setElaboratedKeywordLoc(TypenameLoc); - TL.setQualifierLoc(QualifierLoc); - TL.getNamedTypeLoc().castAs<TypeSpecTypeLoc>().setNameLoc(IdLoc); - } - return CreateParsedType(T, TSI); } @@ -10166,6 +10219,35 @@ static bool isEnableIf(NestedNameSpecifierLoc NNS, const IdentifierInfo &II, return true; } +QualType +Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, + SourceLocation KeywordLoc, + NestedNameSpecifierLoc QualifierLoc, + const IdentifierInfo &II, + SourceLocation IILoc, + TypeSourceInfo **TSI, + bool DeducedTSTContext) { + QualType T = CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, II, IILoc, + DeducedTSTContext); + if (T.isNull()) + return QualType(); + + *TSI = Context.CreateTypeSourceInfo(T); + if (isa<DependentNameType>(T)) { + DependentNameTypeLoc TL = + (*TSI)->getTypeLoc().castAs<DependentNameTypeLoc>(); + TL.setElaboratedKeywordLoc(KeywordLoc); + TL.setQualifierLoc(QualifierLoc); + TL.setNameLoc(IILoc); + } else { + ElaboratedTypeLoc TL = (*TSI)->getTypeLoc().castAs<ElaboratedTypeLoc>(); + TL.setElaboratedKeywordLoc(KeywordLoc); + TL.setQualifierLoc(QualifierLoc); + TL.getNamedTypeLoc().castAs<TypeSpecTypeLoc>().setNameLoc(IILoc); + } + return T; +} + /// Build the type that describes a C++ typename specifier, /// e.g., "typename T::type". QualType @@ -10173,32 +10255,38 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, SourceLocation KeywordLoc, NestedNameSpecifierLoc QualifierLoc, const IdentifierInfo &II, - SourceLocation IILoc) { + SourceLocation IILoc, bool DeducedTSTContext) { CXXScopeSpec SS; SS.Adopt(QualifierLoc); - DeclContext *Ctx = computeDeclContext(SS); - if (!Ctx) { - // If the nested-name-specifier is dependent and couldn't be - // resolved to a type, build a typename type. - assert(QualifierLoc.getNestedNameSpecifier()->isDependent()); - return Context.getDependentNameType(Keyword, - QualifierLoc.getNestedNameSpecifier(), - &II); + DeclContext *Ctx = nullptr; + if (QualifierLoc) { + Ctx = computeDeclContext(SS); + if (!Ctx) { + // If the nested-name-specifier is dependent and couldn't be + // resolved to a type, build a typename type. + assert(QualifierLoc.getNestedNameSpecifier()->isDependent()); + return Context.getDependentNameType(Keyword, + QualifierLoc.getNestedNameSpecifier(), + &II); + } + + // If the nested-name-specifier refers to the current instantiation, + // the "typename" keyword itself is superfluous. In C++03, the + // program is actually ill-formed. However, DR 382 (in C++0x CD1) + // allows such extraneous "typename" keywords, and we retroactively + // apply this DR to C++03 code with only a warning. In any case we continue. + + if (RequireCompleteDeclContext(SS, Ctx)) + return QualType(); } - // If the nested-name-specifier refers to the current instantiation, - // the "typename" keyword itself is superfluous. In C++03, the - // program is actually ill-formed. However, DR 382 (in C++0x CD1) - // allows such extraneous "typename" keywords, and we retroactively - // apply this DR to C++03 code with only a warning. In any case we continue. - - if (RequireCompleteDeclContext(SS, Ctx)) - return QualType(); - DeclarationName Name(&II); LookupResult Result(*this, Name, IILoc, LookupOrdinaryName); - LookupQualifiedName(Result, Ctx, SS); + if (Ctx) + LookupQualifiedName(Result, Ctx, SS); + else + LookupName(Result, CurScope); unsigned DiagID = 0; Decl *Referenced = nullptr; switch (Result.getResultKind()) { @@ -10207,7 +10295,7 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, // a more specific diagnostic. SourceRange CondRange; Expr *Cond = nullptr; - if (isEnableIf(QualifierLoc, II, CondRange, Cond)) { + if (Ctx && isEnableIf(QualifierLoc, II, CondRange, Cond)) { // If we have a condition, narrow it down to the specific failed // condition. if (Cond) { @@ -10223,12 +10311,14 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, return QualType(); } - Diag(CondRange.getBegin(), diag::err_typename_nested_not_found_enable_if) + Diag(CondRange.getBegin(), + diag::err_typename_nested_not_found_enable_if) << Ctx << CondRange; return QualType(); } - DiagID = diag::err_typename_nested_not_found; + DiagID = Ctx ? diag::err_typename_nested_not_found + : diag::err_unknown_typename; break; } @@ -10294,6 +10384,19 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, // is a placeholder for a deduced class type [...]. if (getLangOpts().CPlusPlus17) { if (auto *TD = getAsTypeTemplateDecl(Result.getFoundDecl())) { + if (!DeducedTSTContext) { + QualType T(QualifierLoc + ? QualifierLoc.getNestedNameSpecifier()->getAsType() + : nullptr, 0); + if (!T.isNull()) + Diag(IILoc, diag::err_dependent_deduced_tst) + << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << T; + else + Diag(IILoc, diag::err_deduced_tst) + << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)); + Diag(TD->getLocation(), diag::note_template_decl_here); + return QualType(); + } return Context.getElaboratedType( Keyword, QualifierLoc.getNestedNameSpecifier(), Context.getDeducedTemplateSpecializationType(TemplateName(TD), @@ -10301,12 +10404,14 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, } } - DiagID = diag::err_typename_nested_not_type; + DiagID = Ctx ? diag::err_typename_nested_not_type + : diag::err_typename_not_type; Referenced = Result.getFoundDecl(); break; case LookupResult::FoundOverloaded: - DiagID = diag::err_typename_nested_not_type; + DiagID = Ctx ? diag::err_typename_nested_not_type + : diag::err_typename_not_type; Referenced = *Result.begin(); break; @@ -10318,9 +10423,14 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, // type. Emit an appropriate diagnostic and return an error. SourceRange FullRange(KeywordLoc.isValid() ? KeywordLoc : SS.getBeginLoc(), IILoc); - Diag(IILoc, DiagID) << FullRange << Name << Ctx; + if (Ctx) + Diag(IILoc, DiagID) << FullRange << Name << Ctx; + else + Diag(IILoc, DiagID) << FullRange << Name; if (Referenced) - Diag(Referenced->getLocation(), diag::note_typename_refers_here) + Diag(Referenced->getLocation(), + Ctx ? diag::note_typename_member_refers_here + : diag::note_typename_refers_here) << Name; return QualType(); } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 1b9f1b2144d1a..394c81c827946 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -724,38 +724,48 @@ private: // Compute the set of template parameter indices that correspond to // parameter packs expanded by the pack expansion. llvm::SmallBitVector SawIndices(TemplateParams->size()); + llvm::SmallVector<TemplateArgument, 4> ExtraDeductions; auto AddPack = [&](unsigned Index) { if (SawIndices[Index]) return; SawIndices[Index] = true; addPack(Index); + + // Deducing a parameter pack that is a pack expansion also constrains the + // packs appearing in that parameter to have the same deduced arity. Also, + // in C++17 onwards, deducing a non-type template parameter deduces its + // type, so we need to collect the pending deduced values for those packs. + if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>( + TemplateParams->getParam(Index))) { + if (auto *Expansion = dyn_cast<PackExpansionType>(NTTP->getType())) + ExtraDeductions.push_back(Expansion->getPattern()); + } + // FIXME: Also collect the unexpanded packs in any type and template + // parameter packs that are pack expansions. }; - // First look for unexpanded packs in the pattern. - SmallVector<UnexpandedParameterPack, 2> Unexpanded; - S.collectUnexpandedParameterPacks(Pattern, Unexpanded); - for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) { - unsigned Depth, Index; - std::tie(Depth, Index) = getDepthAndIndex(Unexpanded[I]); - if (Depth == Info.getDeducedDepth()) - AddPack(Index); - } + auto Collect = [&](TemplateArgument Pattern) { + SmallVector<UnexpandedParameterPack, 2> Unexpanded; + S.collectUnexpandedParameterPacks(Pattern, Unexpanded); + for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) { + unsigned Depth, Index; + std::tie(Depth, Index) = getDepthAndIndex(Unexpanded[I]); + if (Depth == Info.getDeducedDepth()) + AddPack(Index); + } + }; + + // Look for unexpanded packs in the pattern. + Collect(Pattern); assert(!Packs.empty() && "Pack expansion without unexpanded packs?"); unsigned NumNamedPacks = Packs.size(); - // We can also have deduced template parameters that do not actually - // appear in the pattern, but can be deduced by it (the type of a non-type - // template parameter pack, in particular). These won't have prevented us - // from partially expanding the pack. - llvm::SmallBitVector Used(TemplateParams->size()); - MarkUsedTemplateParameters(S.Context, Pattern, /*OnlyDeduced*/true, - Info.getDeducedDepth(), Used); - for (int Index = Used.find_first(); Index != -1; - Index = Used.find_next(Index)) - if (TemplateParams->getParam(Index)->isParameterPack()) - AddPack(Index); + // Also look for unexpanded packs that are indirectly deduced by deducing + // the sizes of the packs in this pattern. + while (!ExtraDeductions.empty()) + Collect(ExtraDeductions.pop_back_val()); return NumNamedPacks; } @@ -4404,9 +4414,10 @@ namespace { QualType Result = SemaRef.Context.getAutoType( Replacement, TL.getTypePtr()->getKeyword(), Replacement.isNull(), - ReplacementIsPack); + ReplacementIsPack, TL.getTypePtr()->getTypeConstraintConcept(), + TL.getTypePtr()->getTypeConstraintArguments()); auto NewTL = TLB.push<AutoTypeLoc>(Result); - NewTL.setNameLoc(TL.getNameLoc()); + NewTL.copy(TL); return Result; } @@ -4441,9 +4452,10 @@ namespace { Sema::DeduceAutoResult Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init, QualType &Result, - Optional<unsigned> DependentDeductionDepth) { + Optional<unsigned> DependentDeductionDepth, + bool IgnoreConstraints) { return DeduceAutoType(Type->getTypeLoc(), Init, Result, - DependentDeductionDepth); + DependentDeductionDepth, IgnoreConstraints); } /// Attempt to produce an informative diagostic explaining why auto deduction @@ -4471,6 +4483,49 @@ static bool diagnoseAutoDeductionFailure(Sema &S, } } +static Sema::DeduceAutoResult +CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, + AutoTypeLoc TypeLoc, QualType Deduced) { + ConstraintSatisfaction Satisfaction; + ConceptDecl *Concept = Type.getTypeConstraintConcept(); + TemplateArgumentListInfo TemplateArgs(TypeLoc.getLAngleLoc(), + TypeLoc.getRAngleLoc()); + TemplateArgs.addArgument( + TemplateArgumentLoc(TemplateArgument(Deduced), + S.Context.getTrivialTypeSourceInfo( + Deduced, TypeLoc.getNameLoc()))); + for (unsigned I = 0, C = TypeLoc.getNumArgs(); I != C; ++I) + TemplateArgs.addArgument(TypeLoc.getArgLoc(I)); + + llvm::SmallVector<TemplateArgument, 4> Converted; + if (S.CheckTemplateArgumentList(Concept, SourceLocation(), TemplateArgs, + /*PartialTemplateArgs=*/false, Converted)) + return Sema::DAR_FailedAlreadyDiagnosed; + if (S.CheckConstraintSatisfaction(Concept, {Concept->getConstraintExpr()}, + Converted, TypeLoc.getLocalSourceRange(), + Satisfaction)) + return Sema::DAR_FailedAlreadyDiagnosed; + if (!Satisfaction.IsSatisfied) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + OS << "'" << Concept->getName(); + if (TypeLoc.hasExplicitTemplateArgs()) { + OS << "<"; + for (const auto &Arg : Type.getTypeConstraintArguments()) + Arg.print(S.getPrintingPolicy(), OS); + OS << ">"; + } + OS << "'"; + OS.flush(); + S.Diag(TypeLoc.getConceptNameLoc(), + diag::err_placeholder_constraints_not_satisfied) + << Deduced << Buf << TypeLoc.getLocalSourceRange(); + S.DiagnoseUnsatisfiedConstraint(Satisfaction); + return Sema::DAR_FailedAlreadyDiagnosed; + } + return Sema::DAR_Succeeded; +} + /// Deduce the type for an auto type-specifier (C++11 [dcl.spec.auto]p6) /// /// Note that this is done even if the initializer is dependent. (This is @@ -4485,9 +4540,12 @@ static bool diagnoseAutoDeductionFailure(Sema &S, /// dependent cases. This is necessary for template partial ordering with /// 'auto' template parameters. The value specified is the template /// parameter depth at which we should perform 'auto' deduction. +/// \param IgnoreConstraints Set if we should not fail if the deduced type does +/// not satisfy the type-constraint in the auto type. Sema::DeduceAutoResult Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, - Optional<unsigned> DependentDeductionDepth) { + Optional<unsigned> DependentDeductionDepth, + bool IgnoreConstraints) { if (Init->getType()->isNonOverloadPlaceholderType()) { ExprResult NonPlaceholder = CheckPlaceholderExpr(Init); if (NonPlaceholder.isInvalid()) @@ -4528,6 +4586,14 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, return DAR_FailedAlreadyDiagnosed; // FIXME: Support a non-canonical deduced type for 'auto'. Deduced = Context.getCanonicalType(Deduced); + if (AT->isConstrained() && !IgnoreConstraints) { + auto ConstraintsResult = + CheckDeducedPlaceholderConstraints(*this, *AT, + Type.getContainedAutoTypeLoc(), + Deduced); + if (ConstraintsResult != DAR_Succeeded) + return ConstraintsResult; + } Result = SubstituteDeducedTypeTransform(*this, Deduced).Apply(Type); if (Result.isNull()) return DAR_FailedAlreadyDiagnosed; @@ -4635,6 +4701,17 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, return DAR_FailedAlreadyDiagnosed; } + if (const auto *AT = Type.getType()->getAs<AutoType>()) { + if (AT->isConstrained() && !IgnoreConstraints) { + auto ConstraintsResult = + CheckDeducedPlaceholderConstraints(*this, *AT, + Type.getContainedAutoTypeLoc(), + DeducedType); + if (ConstraintsResult != DAR_Succeeded) + return ConstraintsResult; + } + } + Result = SubstituteDeducedTypeTransform(*this, DeducedType).Apply(Type); if (Result.isNull()) return DAR_FailedAlreadyDiagnosed; diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index af41e231134d0..39bc28d62305b 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -26,6 +26,7 @@ #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/TemplateInstCallback.h" +#include "clang/Sema/SemaConcept.h" #include "llvm/Support/TimeProfiler.h" using namespace clang; @@ -199,8 +200,10 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { case DeducedTemplateArgumentSubstitution: case PriorTemplateArgumentSubstitution: case ConstraintsCheck: + case NestedRequirementConstraintsCheck: return true; + case RequirementInstantiation: case DefaultTemplateArgumentChecking: case DeclaringSpecialMember: case DeclaringImplicitEqualityComparison: @@ -247,7 +250,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Inst.InstantiationRange = InstantiationRange; SemaRef.pushCodeSynthesisContext(Inst); - AlreadyInstantiating = + AlreadyInstantiating = !Inst.Entity ? false : !SemaRef.InstantiatingSpecializations .insert(std::make_pair(Inst.Entity->getCanonicalDecl(), Inst.Kind)) .second; @@ -366,6 +369,26 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Sema::InstantiatingTemplate::InstantiatingTemplate( Sema &SemaRef, SourceLocation PointOfInstantiation, + concepts::Requirement *Req, sema::TemplateDeductionInfo &DeductionInfo, + SourceRange InstantiationRange) + : InstantiatingTemplate( + SemaRef, CodeSynthesisContext::RequirementInstantiation, + PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr, + /*Template=*/nullptr, /*TemplateArgs=*/None, &DeductionInfo) {} + + +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation PointOfInstantiation, + concepts::NestedRequirement *Req, ConstraintsCheck, + SourceRange InstantiationRange) + : InstantiatingTemplate( + SemaRef, CodeSynthesisContext::NestedRequirementConstraintsCheck, + PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr, + /*Template=*/nullptr, /*TemplateArgs=*/None) {} + + +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation PointOfInstantiation, ConstraintsCheck, NamedDecl *Template, ArrayRef<TemplateArgument> TemplateArgs, SourceRange InstantiationRange) : InstantiatingTemplate( @@ -446,8 +469,9 @@ void Sema::InstantiatingTemplate::Clear() { if (!Invalid) { if (!AlreadyInstantiating) { auto &Active = SemaRef.CodeSynthesisContexts.back(); - SemaRef.InstantiatingSpecializations.erase( - std::make_pair(Active.Entity, Active.Kind)); + if (Active.Entity) + SemaRef.InstantiatingSpecializations.erase( + std::make_pair(Active.Entity, Active.Kind)); } atTemplateEnd(SemaRef.TemplateInstCallbacks, SemaRef, @@ -684,6 +708,18 @@ void Sema::PrintInstantiationStack() { << Active->InstantiationRange; break; + case CodeSynthesisContext::RequirementInstantiation: + Diags.Report(Active->PointOfInstantiation, + diag::note_template_requirement_instantiation_here) + << Active->InstantiationRange; + break; + + case CodeSynthesisContext::NestedRequirementConstraintsCheck: + Diags.Report(Active->PointOfInstantiation, + diag::note_nested_requirement_here) + << Active->InstantiationRange; + break; + case CodeSynthesisContext::DeclaringSpecialMember: Diags.Report(Active->PointOfInstantiation, diag::note_in_declaration_of_implicit_special_member) @@ -788,6 +824,7 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const { case CodeSynthesisContext::ConstraintsCheck: case CodeSynthesisContext::ParameterMappingSubstitution: case CodeSynthesisContext::ConstraintNormalization: + case CodeSynthesisContext::NestedRequirementConstraintsCheck: // This is a template instantiation, so there is no SFINAE. return None; @@ -802,9 +839,10 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const { case CodeSynthesisContext::ExplicitTemplateArgumentSubstitution: case CodeSynthesisContext::DeducedTemplateArgumentSubstitution: case CodeSynthesisContext::ConstraintSubstitution: - // We're either substituting explicitly-specified template arguments - // or deduced template arguments or a constraint expression, so SFINAE - // applies. + case CodeSynthesisContext::RequirementInstantiation: + // We're either substituting explicitly-specified template arguments, + // deduced template arguments, a constraint expression or a requirement + // in a requires expression, so SFINAE applies. assert(Active->DeductionInfo && "Missing deduction info pointer"); return Active->DeductionInfo; @@ -1056,6 +1094,41 @@ namespace { return TreeTransform<TemplateInstantiator>::TransformLambdaExpr(E); } + ExprResult TransformRequiresExpr(RequiresExpr *E) { + LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); + return TreeTransform<TemplateInstantiator>::TransformRequiresExpr(E); + } + + bool TransformRequiresExprRequirements( + ArrayRef<concepts::Requirement *> Reqs, + SmallVectorImpl<concepts::Requirement *> &Transformed) { + bool SatisfactionDetermined = false; + for (concepts::Requirement *Req : Reqs) { + concepts::Requirement *TransReq = nullptr; + if (!SatisfactionDetermined) { + if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) + TransReq = TransformTypeRequirement(TypeReq); + else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) + TransReq = TransformExprRequirement(ExprReq); + else + TransReq = TransformNestedRequirement( + cast<concepts::NestedRequirement>(Req)); + if (!TransReq) + return true; + if (!TransReq->isDependent() && !TransReq->isSatisfied()) + // [expr.prim.req]p6 + // [...] The substitution and semantic constraint checking + // proceeds in lexical order and stops when a condition that + // determines the result of the requires-expression is + // encountered. [..] + SatisfactionDetermined = true; + } else + TransReq = Req; + Transformed.push_back(TransReq); + } + return false; + } + TemplateParameterList *TransformTemplateParameterList( TemplateParameterList *OrigTPL) { if (!OrigTPL || !OrigTPL->size()) return OrigTPL; @@ -1065,6 +1138,14 @@ namespace { /* DeclContext *Owner */ Owner, TemplateArgs); return DeclInstantiator.SubstTemplateParams(OrigTPL); } + + concepts::TypeRequirement * + TransformTypeRequirement(concepts::TypeRequirement *Req); + concepts::ExprRequirement * + TransformExprRequirement(concepts::ExprRequirement *Req); + concepts::NestedRequirement * + TransformNestedRequirement(concepts::NestedRequirement *Req); + private: ExprResult transformNonTypeTemplateParmRef(NonTypeTemplateParmDecl *parm, SourceLocation loc, @@ -1669,6 +1750,163 @@ TemplateInstantiator::TransformSubstTemplateTypeParmPackType( return Result; } +template<typename EntityPrinter> +static concepts::Requirement::SubstitutionDiagnostic * +createSubstDiag(Sema &S, TemplateDeductionInfo &Info, EntityPrinter Printer) { + SmallString<128> Message; + SourceLocation ErrorLoc; + if (Info.hasSFINAEDiagnostic()) { + PartialDiagnosticAt PDA(SourceLocation(), + PartialDiagnostic::NullDiagnostic{}); + Info.takeSFINAEDiagnostic(PDA); + PDA.second.EmitToString(S.getDiagnostics(), Message); + ErrorLoc = PDA.first; + } else { + ErrorLoc = Info.getLocation(); + } + char *MessageBuf = new (S.Context) char[Message.size()]; + std::copy(Message.begin(), Message.end(), MessageBuf); + SmallString<128> Entity; + llvm::raw_svector_ostream OS(Entity); + Printer(OS); + char *EntityBuf = new (S.Context) char[Entity.size()]; + std::copy(Entity.begin(), Entity.end(), EntityBuf); + return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{ + StringRef(EntityBuf, Entity.size()), ErrorLoc, + StringRef(MessageBuf, Message.size())}; +} + +concepts::TypeRequirement * +TemplateInstantiator::TransformTypeRequirement(concepts::TypeRequirement *Req) { + if (!Req->isDependent() && !AlwaysRebuild()) + return Req; + if (Req->isSubstitutionFailure()) { + if (AlwaysRebuild()) + return RebuildTypeRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + + Sema::SFINAETrap Trap(SemaRef); + TemplateDeductionInfo Info(Req->getType()->getTypeLoc().getBeginLoc()); + Sema::InstantiatingTemplate TypeInst(SemaRef, + Req->getType()->getTypeLoc().getBeginLoc(), Req, Info, + Req->getType()->getTypeLoc().getSourceRange()); + if (TypeInst.isInvalid()) + return nullptr; + TypeSourceInfo *TransType = TransformType(Req->getType()); + if (!TransType || Trap.hasErrorOccurred()) + return RebuildTypeRequirement(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_ostream& OS) { + Req->getType()->getType().print(OS, SemaRef.getPrintingPolicy()); + })); + return RebuildTypeRequirement(TransType); +} + +concepts::ExprRequirement * +TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) { + if (!Req->isDependent() && !AlwaysRebuild()) + return Req; + + Sema::SFINAETrap Trap(SemaRef); + TemplateDeductionInfo Info(Req->getExpr()->getBeginLoc()); + + llvm::PointerUnion<Expr *, concepts::Requirement::SubstitutionDiagnostic *> + TransExpr; + if (Req->isExprSubstitutionFailure()) + TransExpr = Req->getExprSubstitutionDiagnostic(); + else { + Sema::InstantiatingTemplate ExprInst(SemaRef, Req->getExpr()->getBeginLoc(), + Req, Info, + Req->getExpr()->getSourceRange()); + if (ExprInst.isInvalid()) + return nullptr; + ExprResult TransExprRes = TransformExpr(Req->getExpr()); + if (TransExprRes.isInvalid() || Trap.hasErrorOccurred()) + TransExpr = createSubstDiag(SemaRef, Info, + [&] (llvm::raw_ostream& OS) { + Req->getExpr()->printPretty(OS, nullptr, + SemaRef.getPrintingPolicy()); + }); + else + TransExpr = TransExprRes.get(); + } + + llvm::Optional<concepts::ExprRequirement::ReturnTypeRequirement> TransRetReq; + const auto &RetReq = Req->getReturnTypeRequirement(); + if (RetReq.isEmpty()) + TransRetReq.emplace(); + else if (RetReq.isSubstitutionFailure()) + TransRetReq.emplace(RetReq.getSubstitutionDiagnostic()); + else if (RetReq.isTypeConstraint()) { + TemplateParameterList *OrigTPL = + RetReq.getTypeConstraintTemplateParameterList(); + Sema::InstantiatingTemplate TPLInst(SemaRef, OrigTPL->getTemplateLoc(), + Req, Info, OrigTPL->getSourceRange()); + if (TPLInst.isInvalid()) + return nullptr; + TemplateParameterList *TPL = + TransformTemplateParameterList(OrigTPL); + if (!TPL) + TransRetReq.emplace(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_ostream& OS) { + RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint() + ->printPretty(OS, nullptr, SemaRef.getPrintingPolicy()); + })); + else { + TPLInst.Clear(); + TransRetReq.emplace(TPL); + } + } + assert(TransRetReq.hasValue() && + "All code paths leading here must set TransRetReq"); + if (Expr *E = TransExpr.dyn_cast<Expr *>()) + return RebuildExprRequirement(E, Req->isSimple(), Req->getNoexceptLoc(), + std::move(*TransRetReq)); + return RebuildExprRequirement( + TransExpr.get<concepts::Requirement::SubstitutionDiagnostic *>(), + Req->isSimple(), Req->getNoexceptLoc(), std::move(*TransRetReq)); +} + +concepts::NestedRequirement * +TemplateInstantiator::TransformNestedRequirement( + concepts::NestedRequirement *Req) { + if (!Req->isDependent() && !AlwaysRebuild()) + return Req; + if (Req->isSubstitutionFailure()) { + if (AlwaysRebuild()) + return RebuildNestedRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + Sema::InstantiatingTemplate ReqInst(SemaRef, + Req->getConstraintExpr()->getBeginLoc(), Req, + Sema::InstantiatingTemplate::ConstraintsCheck{}, + Req->getConstraintExpr()->getSourceRange()); + + ExprResult TransConstraint; + TemplateDeductionInfo Info(Req->getConstraintExpr()->getBeginLoc()); + { + EnterExpressionEvaluationContext ContextRAII( + SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated); + Sema::SFINAETrap Trap(SemaRef); + Sema::InstantiatingTemplate ConstrInst(SemaRef, + Req->getConstraintExpr()->getBeginLoc(), Req, Info, + Req->getConstraintExpr()->getSourceRange()); + if (ConstrInst.isInvalid()) + return nullptr; + TransConstraint = TransformExpr(Req->getConstraintExpr()); + if (TransConstraint.isInvalid() || Trap.hasErrorOccurred()) + return RebuildNestedRequirement(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_ostream& OS) { + Req->getConstraintExpr()->printPretty(OS, nullptr, + SemaRef.getPrintingPolicy()); + })); + } + return RebuildNestedRequirement(TransConstraint.get()); +} + + /// Perform substitution on the type T with a given set of template /// arguments. /// diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 64500d0a26d54..fbbab8f007039 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1848,6 +1848,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl( // FIXME: Concepts: Do not substitute into constraint expressions Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); if (TrailingRequiresClause) { + EnterExpressionEvaluationContext ConstantEvaluated( + SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, TemplateArgs); if (SubstRC.isInvalid()) @@ -2186,6 +2188,8 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( // FIXME: Concepts: Do not substitute into constraint expressions Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); if (TrailingRequiresClause) { + EnterExpressionEvaluationContext ConstantEvaluated( + SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, TemplateArgs); if (SubstRC.isInvalid()) @@ -2685,6 +2689,16 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl( D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), D->getPosition(), D->getIdentifier(), T, D->isParameterPack(), DI); + if (AutoTypeLoc AutoLoc = DI->getTypeLoc().getContainedAutoTypeLoc()) + if (AutoLoc.isConstrained()) + if (SemaRef.AttachTypeConstraint( + AutoLoc, Param, + IsExpandedParameterPack + ? DI->getTypeLoc().getAs<PackExpansionTypeLoc>() + .getEllipsisLoc() + : SourceLocation())) + Invalid = true; + Param->setAccess(AS_public); Param->setImplicit(D->isImplicit()); if (Invalid) @@ -3600,6 +3614,12 @@ Decl *TemplateDeclInstantiator::VisitConceptDecl(ConceptDecl *D) { llvm_unreachable("Concept definitions cannot reside inside a template"); } +Decl * +TemplateDeclInstantiator::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) { + return RequiresExprBodyDecl::Create(SemaRef.Context, D->getDeclContext(), + D->getBeginLoc()); +} + Decl *TemplateDeclInstantiator::VisitDecl(Decl *D) { llvm_unreachable("Unexpected decl"); } @@ -3713,6 +3733,8 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) { // checking satisfaction. Expr *InstRequiresClause = nullptr; if (Expr *E = L->getRequiresClause()) { + EnterExpressionEvaluationContext ConstantEvaluated( + SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); ExprResult Res = SemaRef.SubstExpr(E, TemplateArgs); if (Res.isInvalid() || !Res.isUsable()) { return nullptr; @@ -4236,9 +4258,9 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints( MLTAL.getInnermost(), SourceRange()); if (Inst.isInvalid()) return true; - if (addInstantiatedParametersToScope(*this, Decl, - Decl->getTemplateInstantiationPattern(), - Scope, MLTAL)) + if (addInstantiatedParametersToScope( + *this, Decl, Decl->getPrimaryTemplate()->getTemplatedDecl(), + Scope, MLTAL)) return true; } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 3884fdae8fe72..93ddd047e09be 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "TypeLocBuilder.h" +#include "TreeTransform.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTMutationListener.h" @@ -27,6 +28,7 @@ #include "clang/Sema/DeclSpec.h" #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" @@ -1251,6 +1253,26 @@ getImageAccess(const ParsedAttributesView &Attrs) { return OpenCLAccessAttr::Keyword_read_only; } +static QualType ConvertConstrainedAutoDeclSpecToType(Sema &S, DeclSpec &DS, + AutoTypeKeyword AutoKW) { + assert(DS.isConstrainedAuto()); + TemplateIdAnnotation *TemplateId = DS.getRepAsTemplateId(); + TemplateArgumentListInfo TemplateArgsInfo; + TemplateArgsInfo.setLAngleLoc(TemplateId->LAngleLoc); + TemplateArgsInfo.setRAngleLoc(TemplateId->RAngleLoc); + ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + S.translateTemplateArguments(TemplateArgsPtr, TemplateArgsInfo); + llvm::SmallVector<TemplateArgument, 8> TemplateArgs; + for (auto &ArgLoc : TemplateArgsInfo.arguments()) + TemplateArgs.push_back(ArgLoc.getArgument()); + return S.Context.getAutoType(QualType(), AutoTypeKeyword::Auto, false, + /*IsPack=*/false, + cast<ConceptDecl>(TemplateId->Template.get() + .getAsTemplateDecl()), + TemplateArgs); +} + /// Convert the specified declspec to the appropriate type /// object. /// \param state Specifies the declarator containing the declaration specifier @@ -1595,6 +1617,11 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { break; case DeclSpec::TST_auto: + if (DS.isConstrainedAuto()) { + Result = ConvertConstrainedAutoDeclSpecToType(S, DS, + AutoTypeKeyword::Auto); + break; + } Result = Context.getAutoType(QualType(), AutoTypeKeyword::Auto, false); break; @@ -1603,6 +1630,12 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { break; case DeclSpec::TST_decltype_auto: + if (DS.isConstrainedAuto()) { + Result = + ConvertConstrainedAutoDeclSpecToType(S, DS, + AutoTypeKeyword::DecltypeAuto); + break; + } Result = Context.getAutoType(QualType(), AutoTypeKeyword::DecltypeAuto, /*IsDependent*/ false); break; @@ -2921,6 +2954,87 @@ static void diagnoseRedundantReturnTypeQualifiers(Sema &S, QualType RetTy, D.getDeclSpec().getUnalignedSpecLoc()); } +static void CopyTypeConstraintFromAutoType(Sema &SemaRef, const AutoType *Auto, + AutoTypeLoc AutoLoc, + TemplateTypeParmDecl *TP, + SourceLocation EllipsisLoc) { + + TemplateArgumentListInfo TAL(AutoLoc.getLAngleLoc(), AutoLoc.getRAngleLoc()); + for (unsigned Idx = 0; Idx < AutoLoc.getNumArgs(); ++Idx) + TAL.addArgument(AutoLoc.getArgLoc(Idx)); + + SemaRef.AttachTypeConstraint( + AutoLoc.getNestedNameSpecifierLoc(), AutoLoc.getConceptNameInfo(), + AutoLoc.getNamedConcept(), + AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr, TP, EllipsisLoc); +} + +static QualType InventTemplateParameter( + TypeProcessingState &state, QualType T, TypeSourceInfo *TSI, AutoType *Auto, + InventedTemplateParameterInfo &Info) { + Sema &S = state.getSema(); + Declarator &D = state.getDeclarator(); + + const unsigned TemplateParameterDepth = Info.AutoTemplateParameterDepth; + const unsigned AutoParameterPosition = Info.TemplateParams.size(); + const bool IsParameterPack = D.hasEllipsis(); + + // If auto is mentioned in a lambda parameter or abbreviated function + // template context, convert it to a template parameter type. + + // Create the TemplateTypeParmDecl here to retrieve the corresponding + // template parameter type. Template parameters are temporarily added + // to the TU until the associated TemplateDecl is created. + TemplateTypeParmDecl *InventedTemplateParam = + TemplateTypeParmDecl::Create( + S.Context, S.Context.getTranslationUnitDecl(), + /*KeyLoc=*/D.getDeclSpec().getTypeSpecTypeLoc(), + /*NameLoc=*/D.getIdentifierLoc(), + TemplateParameterDepth, AutoParameterPosition, + S.InventAbbreviatedTemplateParameterTypeName( + D.getIdentifier(), AutoParameterPosition), false, + IsParameterPack, /*HasTypeConstraint=*/Auto->isConstrained()); + InventedTemplateParam->setImplicit(); + Info.TemplateParams.push_back(InventedTemplateParam); + // Attach type constraints + if (Auto->isConstrained()) { + if (TSI) { + CopyTypeConstraintFromAutoType( + S, Auto, TSI->getTypeLoc().getContainedAutoTypeLoc(), + InventedTemplateParam, D.getEllipsisLoc()); + } else { + TemplateIdAnnotation *TemplateId = D.getDeclSpec().getRepAsTemplateId(); + TemplateArgumentListInfo TemplateArgsInfo; + if (TemplateId->LAngleLoc.isValid()) { + ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + S.translateTemplateArguments(TemplateArgsPtr, TemplateArgsInfo); + } + S.AttachTypeConstraint( + D.getDeclSpec().getTypeSpecScope().getWithLocInContext(S.Context), + DeclarationNameInfo(DeclarationName(TemplateId->Name), + TemplateId->TemplateNameLoc), + cast<ConceptDecl>(TemplateId->Template.get().getAsTemplateDecl()), + TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr, + InventedTemplateParam, D.getEllipsisLoc()); + } + } + + // If TSI is nullptr, this is a constrained declspec auto and the type + // constraint will be attached later in TypeSpecLocFiller + + // Replace the 'auto' in the function parameter with this invented + // template type parameter. + // FIXME: Retain some type sugar to indicate that this was written + // as 'auto'? + return state.ReplaceAutoType( + T, QualType(InventedTemplateParam->getTypeForDecl(), 0)); +} + +static TypeSourceInfo * +GetTypeSourceInfoForDeclarator(TypeProcessingState &State, + QualType T, TypeSourceInfo *ReturnTypeInfo); + static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, TypeSourceInfo *&ReturnTypeInfo) { Sema &SemaRef = state.getSema(); @@ -2991,43 +3105,43 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, break; case DeclaratorContext::ObjCParameterContext: case DeclaratorContext::ObjCResultContext: - case DeclaratorContext::PrototypeContext: Error = 0; break; - case DeclaratorContext::LambdaExprParameterContext: - // In C++14, generic lambdas allow 'auto' in their parameters. - if (!SemaRef.getLangOpts().CPlusPlus14 || - !Auto || Auto->getKeyword() != AutoTypeKeyword::Auto) - Error = 16; - else { - // If auto is mentioned in a lambda parameter context, convert it to a - // template parameter type. - sema::LambdaScopeInfo *LSI = SemaRef.getCurLambda(); - assert(LSI && "No LambdaScopeInfo on the stack!"); - const unsigned TemplateParameterDepth = LSI->AutoTemplateParameterDepth; - const unsigned AutoParameterPosition = LSI->TemplateParams.size(); - const bool IsParameterPack = D.hasEllipsis(); - - // Create the TemplateTypeParmDecl here to retrieve the corresponding - // template parameter type. Template parameters are temporarily added - // to the TU until the associated TemplateDecl is created. - TemplateTypeParmDecl *CorrespondingTemplateParam = - TemplateTypeParmDecl::Create( - SemaRef.Context, SemaRef.Context.getTranslationUnitDecl(), - /*KeyLoc*/ SourceLocation(), /*NameLoc*/ D.getBeginLoc(), - TemplateParameterDepth, AutoParameterPosition, - /*Identifier*/ nullptr, false, IsParameterPack, - /*HasTypeConstraint=*/false); - CorrespondingTemplateParam->setImplicit(); - LSI->TemplateParams.push_back(CorrespondingTemplateParam); - // Replace the 'auto' in the function parameter with this invented - // template type parameter. - // FIXME: Retain some type sugar to indicate that this was written - // as 'auto'. - T = state.ReplaceAutoType( - T, QualType(CorrespondingTemplateParam->getTypeForDecl(), 0)); + case DeclaratorContext::RequiresExprContext: + Error = 22; + break; + case DeclaratorContext::PrototypeContext: + case DeclaratorContext::LambdaExprParameterContext: { + InventedTemplateParameterInfo *Info = nullptr; + if (D.getContext() == DeclaratorContext::PrototypeContext) { + // With concepts we allow 'auto' in function parameters. + if (!SemaRef.getLangOpts().CPlusPlus2a || !Auto || + Auto->getKeyword() != AutoTypeKeyword::Auto) { + Error = 0; + break; + } else if (!SemaRef.getCurScope()->isFunctionDeclarationScope()) { + Error = 21; + break; + } else if (D.hasTrailingReturnType()) { + // This might be OK, but we'll need to convert the trailing return + // type later. + break; + } + + Info = &SemaRef.InventedParameterInfos.back(); + } else { + // In C++14, generic lambdas allow 'auto' in their parameters. + if (!SemaRef.getLangOpts().CPlusPlus14 || !Auto || + Auto->getKeyword() != AutoTypeKeyword::Auto) { + Error = 16; + break; + } + Info = SemaRef.getCurLambda(); + assert(Info && "No LambdaScopeInfo on the stack!"); } + T = InventTemplateParameter(state, T, nullptr, Auto, *Info); break; + } case DeclaratorContext::MemberContext: { if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static || D.isFunctionDeclarator()) @@ -3221,6 +3335,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::ObjCParameterContext: case DeclaratorContext::ObjCResultContext: case DeclaratorContext::KNRTypeListContext: + case DeclaratorContext::RequiresExprContext: // C++ [dcl.fct]p6: // Types shall not be defined in return or parameter types. DiagID = diag::err_type_defined_in_param_type; @@ -4028,10 +4143,6 @@ static bool DiagnoseMultipleAddrSpaceAttributes(Sema &S, LangAS ASOld, return false; } -static TypeSourceInfo * -GetTypeSourceInfoForDeclarator(TypeProcessingState &State, - QualType T, TypeSourceInfo *ReturnTypeInfo); - static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, QualType declSpecType, TypeSourceInfo *TInfo) { @@ -4279,6 +4390,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TypeNameContext: case DeclaratorContext::FunctionalCastContext: + case DeclaratorContext::RequiresExprContext: // Don't infer in these contexts. break; } @@ -4606,7 +4718,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, } else if (D.getContext() != DeclaratorContext::LambdaExprContext && (T.hasQualifiers() || !isa<AutoType>(T) || cast<AutoType>(T)->getKeyword() != - AutoTypeKeyword::Auto)) { + AutoTypeKeyword::Auto || + cast<AutoType>(T)->isConstrained())) { S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), diag::err_trailing_return_without_auto) << T << D.getDeclSpec().getSourceRange(); @@ -4617,7 +4730,12 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // An error occurred parsing the trailing return type. T = Context.IntTy; D.setInvalidType(true); - } + } else if (S.getLangOpts().CPlusPlus2a) + // Handle cases like: `auto f() -> auto` or `auto f() -> C auto`. + if (AutoType *Auto = T->getContainedAutoType()) + if (S.getCurScope()->isFunctionDeclarationScope()) + T = InventTemplateParameter(state, T, TInfo, Auto, + S.InventedParameterInfos.back()); } else { // This function type is not the type of the entity being declared, // so checking the 'auto' is not the responsibility of this chunk. @@ -5227,6 +5345,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, switch (D.getContext()) { case DeclaratorContext::PrototypeContext: case DeclaratorContext::LambdaExprParameterContext: + case DeclaratorContext::RequiresExprContext: // C++0x [dcl.fct]p13: // [...] When it is part of a parameter-declaration-clause, the // parameter pack is a function parameter pack (14.5.3). The type T @@ -5236,7 +5355,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // // We represent function parameter packs as function parameters whose // type is a pack expansion. - if (!T->containsUnexpandedParameterPack()) { + if (!T->containsUnexpandedParameterPack() && + (!LangOpts.CPlusPlus2a || !T->getContainedAutoType())) { S.Diag(D.getEllipsisLoc(), diag::err_function_parameter_pack_without_parameter_packs) << T << D.getSourceRange(); @@ -5444,14 +5564,15 @@ static void fillAttributedTypeLoc(AttributedTypeLoc TL, namespace { class TypeSpecLocFiller : public TypeLocVisitor<TypeSpecLocFiller> { + Sema &SemaRef; ASTContext &Context; TypeProcessingState &State; const DeclSpec &DS; public: - TypeSpecLocFiller(ASTContext &Context, TypeProcessingState &State, + TypeSpecLocFiller(Sema &S, ASTContext &Context, TypeProcessingState &State, const DeclSpec &DS) - : Context(Context), State(State), DS(DS) {} + : SemaRef(S), Context(Context), State(State), DS(DS) {} void VisitAttributedTypeLoc(AttributedTypeLoc TL) { Visit(TL.getModifiedLoc()); @@ -5579,6 +5700,34 @@ namespace { TL.copy( TInfo->getTypeLoc().castAs<DependentTemplateSpecializationTypeLoc>()); } + void VisitAutoTypeLoc(AutoTypeLoc TL) { + assert(DS.getTypeSpecType() == TST_auto || + DS.getTypeSpecType() == TST_decltype_auto || + DS.getTypeSpecType() == TST_auto_type || + DS.getTypeSpecType() == TST_unspecified); + TL.setNameLoc(DS.getTypeSpecTypeLoc()); + if (!DS.isConstrainedAuto()) + return; + TemplateIdAnnotation *TemplateId = DS.getRepAsTemplateId(); + if (DS.getTypeSpecScope().isNotEmpty()) + TL.setNestedNameSpecifierLoc( + DS.getTypeSpecScope().getWithLocInContext(Context)); + else + TL.setNestedNameSpecifierLoc(NestedNameSpecifierLoc()); + TL.setTemplateKWLoc(TemplateId->TemplateKWLoc); + TL.setConceptNameLoc(TemplateId->TemplateNameLoc); + TL.setFoundDecl(nullptr); + TL.setLAngleLoc(TemplateId->LAngleLoc); + TL.setRAngleLoc(TemplateId->RAngleLoc); + if (TemplateId->NumArgs == 0) + return; + TemplateArgumentListInfo TemplateArgsInfo; + ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + SemaRef.translateTemplateArguments(TemplateArgsPtr, TemplateArgsInfo); + for (unsigned I = 0; I < TemplateId->NumArgs; ++I) + TL.setArgLocInfo(I, TemplateArgsInfo.arguments()[I].getLocInfo()); + } void VisitTagTypeLoc(TagTypeLoc TL) { TL.setNameLoc(DS.getTypeSpecTypeNameLoc()); } @@ -5848,7 +5997,7 @@ GetTypeSourceInfoForDeclarator(TypeProcessingState &State, assert(TL.getFullDataSize() == CurrTL.getFullDataSize()); memcpy(CurrTL.getOpaqueData(), TL.getOpaqueData(), TL.getFullDataSize()); } else { - TypeSpecLocFiller(S.Context, State, D.getDeclSpec()).Visit(CurrTL); + TypeSpecLocFiller(S, S.Context, State, D.getDeclSpec()).Visit(CurrTL); } return TInfo; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 3b827fbc950b7..d6105353bbdf4 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -19,6 +19,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" @@ -509,6 +510,15 @@ public: DeclarationNameInfo TransformDeclarationNameInfo(const DeclarationNameInfo &NameInfo); + bool TransformRequiresExprRequirements(ArrayRef<concepts::Requirement *> Reqs, + llvm::SmallVectorImpl<concepts::Requirement *> &Transformed); + concepts::TypeRequirement * + TransformTypeRequirement(concepts::TypeRequirement *Req); + concepts::ExprRequirement * + TransformExprRequirement(concepts::ExprRequirement *Req); + concepts::NestedRequirement * + TransformNestedRequirement(concepts::NestedRequirement *Req); + /// Transform the given template name. /// /// \param SS The nested-name-specifier that qualifies the template @@ -941,12 +951,16 @@ public: /// Build a new C++11 auto type. /// /// By default, builds a new AutoType with the given deduced type. - QualType RebuildAutoType(QualType Deduced, AutoTypeKeyword Keyword) { + QualType RebuildAutoType(QualType Deduced, AutoTypeKeyword Keyword, + ConceptDecl *TypeConstraintConcept, + ArrayRef<TemplateArgument> TypeConstraintArgs) { // Note, IsDependent is always false here: we implicitly convert an 'auto' // which has been deduced to a dependent type into an undeduced 'auto', so // that we'll retry deduction after the transformation. return SemaRef.Context.getAutoType(Deduced, Keyword, - /*IsDependent*/ false); + /*IsDependent*/ false, /*IsPack=*/false, + TypeConstraintConcept, + TypeConstraintArgs); } /// By default, builds a new DeducedTemplateSpecializationType with the given @@ -1056,23 +1070,8 @@ public: } if (Keyword == ETK_None || Keyword == ETK_Typename) { - QualType T = SemaRef.CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, - *Id, IdLoc); - // If a dependent name resolves to a deduced template specialization type, - // check that we're in one of the syntactic contexts permitting it. - if (!DeducedTSTContext) { - if (auto *Deduced = dyn_cast_or_null<DeducedTemplateSpecializationType>( - T.isNull() ? nullptr : T->getContainedDeducedType())) { - SemaRef.Diag(IdLoc, diag::err_dependent_deduced_tst) - << (int)SemaRef.getTemplateNameKindForDiagnostics( - Deduced->getTemplateName()) - << QualType(QualifierLoc.getNestedNameSpecifier()->getAsType(), 0); - if (auto *TD = Deduced->getTemplateName().getAsTemplateDecl()) - SemaRef.Diag(TD->getLocation(), diag::note_template_decl_here); - return QualType(); - } - } - return T; + return SemaRef.CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, + *Id, IdLoc, DeducedTSTContext); } TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForKeyword(Keyword); @@ -3078,7 +3077,56 @@ public: return Result; } - /// \brief Build a new Objective-C boxed expression. + /// \brief Build a new requires expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildRequiresExpr(SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef<ParmVarDecl *> LocalParameters, + ArrayRef<concepts::Requirement *> Requirements, + SourceLocation ClosingBraceLoc) { + return RequiresExpr::Create(SemaRef.Context, RequiresKWLoc, Body, + LocalParameters, Requirements, ClosingBraceLoc); + } + + concepts::TypeRequirement * + RebuildTypeRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + return SemaRef.BuildTypeRequirement(SubstDiag); + } + + concepts::TypeRequirement *RebuildTypeRequirement(TypeSourceInfo *T) { + return SemaRef.BuildTypeRequirement(T); + } + + concepts::ExprRequirement * + RebuildExprRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag, bool IsSimple, + SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement Ret) { + return SemaRef.BuildExprRequirement(SubstDiag, IsSimple, NoexceptLoc, + std::move(Ret)); + } + + concepts::ExprRequirement * + RebuildExprRequirement(Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement Ret) { + return SemaRef.BuildExprRequirement(E, IsSimple, NoexceptLoc, + std::move(Ret)); + } + + concepts::NestedRequirement * + RebuildNestedRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + return SemaRef.BuildNestedRequirement(SubstDiag); + } + + concepts::NestedRequirement *RebuildNestedRequirement(Expr *Constraint) { + return SemaRef.BuildNestedRequirement(Constraint); + } + + /// \brief Build a new Objective-C boxed expression. /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. @@ -4456,7 +4504,10 @@ QualType TreeTransform<Derived>::RebuildQualifiedType(QualType T, Deduced = SemaRef.Context.getQualifiedType(Deduced.getUnqualifiedType(), Qs); T = SemaRef.Context.getAutoType(Deduced, AutoTy->getKeyword(), - AutoTy->isDependentType()); + AutoTy->isDependentType(), + /*isPack=*/false, + AutoTy->getTypeConstraintConcept(), + AutoTy->getTypeConstraintArguments()); } else { // Otherwise, complain about the addition of a qualifier to an // already-qualified type. @@ -5189,21 +5240,29 @@ bool TreeTransform<Derived>::TransformFunctionTypeParams( PackExpansionTypeLoc ExpansionTL = TL.castAs<PackExpansionTypeLoc>(); TypeLoc Pattern = ExpansionTL.getPatternLoc(); SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded); - assert(Unexpanded.size() > 0 && "Could not find parameter packs!"); // Determine whether we should expand the parameter packs. bool ShouldExpand = false; bool RetainExpansion = false; - Optional<unsigned> OrigNumExpansions = - ExpansionTL.getTypePtr()->getNumExpansions(); - NumExpansions = OrigNumExpansions; - if (getDerived().TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(), - Pattern.getSourceRange(), - Unexpanded, - ShouldExpand, - RetainExpansion, - NumExpansions)) { - return true; + Optional<unsigned> OrigNumExpansions; + if (Unexpanded.size() > 0) { + OrigNumExpansions = ExpansionTL.getTypePtr()->getNumExpansions(); + NumExpansions = OrigNumExpansions; + if (getDerived().TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(), + Pattern.getSourceRange(), + Unexpanded, + ShouldExpand, + RetainExpansion, + NumExpansions)) { + return true; + } + } else { +#ifndef NDEBUG + const AutoType *AT = + Pattern.getType().getTypePtr()->getContainedAutoType(); + assert((AT && (!AT->isDeduced() || AT->getDeducedType().isNull())) && + "Could not find parameter packs or undeduced auto type!"); +#endif } if (ShouldExpand) { @@ -5263,6 +5322,9 @@ bool TreeTransform<Derived>::TransformFunctionTypeParams( indexAdjustment, NumExpansions, /*ExpectParameterPack=*/true); + assert(NewParm->isParameterPack() && + "Parameter pack no longer a parameter pack after " + "transformation."); } else { NewParm = getDerived().TransformFunctionTypeParam( OldParm, indexAdjustment, None, /*ExpectParameterPack=*/ false); @@ -5768,32 +5830,6 @@ QualType TreeTransform<Derived>::TransformUnaryTransformType( } template<typename Derived> -QualType TreeTransform<Derived>::TransformAutoType(TypeLocBuilder &TLB, - AutoTypeLoc TL) { - const AutoType *T = TL.getTypePtr(); - QualType OldDeduced = T->getDeducedType(); - QualType NewDeduced; - if (!OldDeduced.isNull()) { - NewDeduced = getDerived().TransformType(OldDeduced); - if (NewDeduced.isNull()) - return QualType(); - } - - QualType Result = TL.getType(); - if (getDerived().AlwaysRebuild() || NewDeduced != OldDeduced || - T->isDependentType()) { - Result = getDerived().RebuildAutoType(NewDeduced, T->getKeyword()); - if (Result.isNull()) - return QualType(); - } - - AutoTypeLoc NewTL = TLB.push<AutoTypeLoc>(Result); - NewTL.setNameLoc(TL.getNameLoc()); - - return Result; -} - -template<typename Derived> QualType TreeTransform<Derived>::TransformDeducedTemplateSpecializationType( TypeLocBuilder &TLB, DeducedTemplateSpecializationTypeLoc TL) { const DeducedTemplateSpecializationType *T = TL.getTypePtr(); @@ -6054,6 +6090,71 @@ QualType TreeTransform<Derived>::TransformPipeType(TypeLocBuilder &TLB, } }; +template<typename Derived> +QualType TreeTransform<Derived>::TransformAutoType(TypeLocBuilder &TLB, + AutoTypeLoc TL) { + const AutoType *T = TL.getTypePtr(); + QualType OldDeduced = T->getDeducedType(); + QualType NewDeduced; + if (!OldDeduced.isNull()) { + NewDeduced = getDerived().TransformType(OldDeduced); + if (NewDeduced.isNull()) + return QualType(); + } + + ConceptDecl *NewCD = nullptr; + TemplateArgumentListInfo NewTemplateArgs; + NestedNameSpecifierLoc NewNestedNameSpec; + if (TL.getTypePtr()->isConstrained()) { + NewCD = cast_or_null<ConceptDecl>( + getDerived().TransformDecl( + TL.getConceptNameLoc(), + TL.getTypePtr()->getTypeConstraintConcept())); + + NewTemplateArgs.setLAngleLoc(TL.getLAngleLoc()); + NewTemplateArgs.setRAngleLoc(TL.getRAngleLoc()); + typedef TemplateArgumentLocContainerIterator<AutoTypeLoc> ArgIterator; + if (getDerived().TransformTemplateArguments(ArgIterator(TL, 0), + ArgIterator(TL, + TL.getNumArgs()), + NewTemplateArgs)) + return QualType(); + + if (TL.getNestedNameSpecifierLoc()) { + NewNestedNameSpec + = getDerived().TransformNestedNameSpecifierLoc( + TL.getNestedNameSpecifierLoc()); + if (!NewNestedNameSpec) + return QualType(); + } + } + + QualType Result = TL.getType(); + if (getDerived().AlwaysRebuild() || NewDeduced != OldDeduced || + T->isDependentType()) { + llvm::SmallVector<TemplateArgument, 4> NewArgList; + NewArgList.reserve(NewArgList.size()); + for (const auto &ArgLoc : NewTemplateArgs.arguments()) + NewArgList.push_back(ArgLoc.getArgument()); + Result = getDerived().RebuildAutoType(NewDeduced, T->getKeyword(), NewCD, + NewArgList); + if (Result.isNull()) + return QualType(); + } + + AutoTypeLoc NewTL = TLB.push<AutoTypeLoc>(Result); + NewTL.setNameLoc(TL.getNameLoc()); + NewTL.setNestedNameSpecifierLoc(NewNestedNameSpec); + NewTL.setTemplateKWLoc(TL.getTemplateKWLoc()); + NewTL.setConceptNameLoc(TL.getConceptNameLoc()); + NewTL.setFoundDecl(TL.getFoundDecl()); + NewTL.setLAngleLoc(TL.getLAngleLoc()); + NewTL.setRAngleLoc(TL.getRAngleLoc()); + for (unsigned I = 0; I < TL.getNumArgs(); ++I) + NewTL.setArgLocInfo(I, NewTemplateArgs.arguments()[I].getLocInfo()); + + return Result; +} template <typename Derived> QualType TreeTransform<Derived>::TransformTemplateSpecializationType( @@ -11179,6 +11280,146 @@ TreeTransform<Derived>::TransformConceptSpecializationExpr( &TransArgs); } +template<typename Derived> +ExprResult +TreeTransform<Derived>::TransformRequiresExpr(RequiresExpr *E) { + SmallVector<ParmVarDecl*, 4> TransParams; + SmallVector<QualType, 4> TransParamTypes; + Sema::ExtParameterInfoBuilder ExtParamInfos; + + // C++2a [expr.prim.req]p2 + // Expressions appearing within a requirement-body are unevaluated operands. + EnterExpressionEvaluationContext Ctx( + SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); + + RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create( + getSema().Context, E->getBody()->getDeclContext(), + E->getBody()->getBeginLoc()); + + Sema::ContextRAII SavedContext(getSema(), Body, /*NewThisContext*/false); + + if (getDerived().TransformFunctionTypeParams(E->getRequiresKWLoc(), + E->getLocalParameters(), + /*ParamTypes=*/nullptr, + /*ParamInfos=*/nullptr, + TransParamTypes, &TransParams, + ExtParamInfos)) + return ExprError(); + + for (ParmVarDecl *Param : TransParams) + Param->setDeclContext(Body); + + SmallVector<concepts::Requirement *, 4> TransReqs; + if (getDerived().TransformRequiresExprRequirements(E->getRequirements(), + TransReqs)) + return ExprError(); + + for (concepts::Requirement *Req : TransReqs) { + if (auto *ER = dyn_cast<concepts::ExprRequirement>(Req)) { + if (ER->getReturnTypeRequirement().isTypeConstraint()) { + ER->getReturnTypeRequirement() + .getTypeConstraintTemplateParameterList()->getParam(0) + ->setDeclContext(Body); + } + } + } + + return getDerived().RebuildRequiresExpr(E->getRequiresKWLoc(), Body, + TransParams, TransReqs, + E->getRBraceLoc()); +} + +template<typename Derived> +bool TreeTransform<Derived>::TransformRequiresExprRequirements( + ArrayRef<concepts::Requirement *> Reqs, + SmallVectorImpl<concepts::Requirement *> &Transformed) { + for (concepts::Requirement *Req : Reqs) { + concepts::Requirement *TransReq = nullptr; + if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) + TransReq = getDerived().TransformTypeRequirement(TypeReq); + else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) + TransReq = getDerived().TransformExprRequirement(ExprReq); + else + TransReq = getDerived().TransformNestedRequirement( + cast<concepts::NestedRequirement>(Req)); + if (!TransReq) + return true; + Transformed.push_back(TransReq); + } + return false; +} + +template<typename Derived> +concepts::TypeRequirement * +TreeTransform<Derived>::TransformTypeRequirement( + concepts::TypeRequirement *Req) { + if (Req->isSubstitutionFailure()) { + if (getDerived().AlwaysRebuild()) + return getDerived().RebuildTypeRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + TypeSourceInfo *TransType = getDerived().TransformType(Req->getType()); + if (!TransType) + return nullptr; + return getDerived().RebuildTypeRequirement(TransType); +} + +template<typename Derived> +concepts::ExprRequirement * +TreeTransform<Derived>::TransformExprRequirement(concepts::ExprRequirement *Req) { + llvm::PointerUnion<Expr *, concepts::Requirement::SubstitutionDiagnostic *> TransExpr; + if (Req->isExprSubstitutionFailure()) + TransExpr = Req->getExprSubstitutionDiagnostic(); + else { + ExprResult TransExprRes = getDerived().TransformExpr(Req->getExpr()); + if (TransExprRes.isInvalid()) + return nullptr; + TransExpr = TransExprRes.get(); + } + + llvm::Optional<concepts::ExprRequirement::ReturnTypeRequirement> TransRetReq; + const auto &RetReq = Req->getReturnTypeRequirement(); + if (RetReq.isEmpty()) + TransRetReq.emplace(); + else if (RetReq.isSubstitutionFailure()) + TransRetReq.emplace(RetReq.getSubstitutionDiagnostic()); + else if (RetReq.isTypeConstraint()) { + TemplateParameterList *OrigTPL = + RetReq.getTypeConstraintTemplateParameterList(); + TemplateParameterList *TPL = + getDerived().TransformTemplateParameterList(OrigTPL); + if (!TPL) + return nullptr; + TransRetReq.emplace(TPL); + } + assert(TransRetReq.hasValue() && + "All code paths leading here must set TransRetReq"); + if (Expr *E = TransExpr.dyn_cast<Expr *>()) + return getDerived().RebuildExprRequirement(E, Req->isSimple(), + Req->getNoexceptLoc(), + std::move(*TransRetReq)); + return getDerived().RebuildExprRequirement( + TransExpr.get<concepts::Requirement::SubstitutionDiagnostic *>(), + Req->isSimple(), Req->getNoexceptLoc(), std::move(*TransRetReq)); +} + +template<typename Derived> +concepts::NestedRequirement * +TreeTransform<Derived>::TransformNestedRequirement( + concepts::NestedRequirement *Req) { + if (Req->isSubstitutionFailure()) { + if (getDerived().AlwaysRebuild()) + return getDerived().RebuildNestedRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + ExprResult TransConstraint = + getDerived().TransformExpr(Req->getConstraintExpr()); + if (TransConstraint.isInvalid()) + return nullptr; + return getDerived().RebuildNestedRequirement(TransConstraint.get()); +} template<typename Derived> ExprResult diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index cdb5b17022c2f..f93f1f77405d3 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -402,6 +402,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::Binding: case Decl::Concept: case Decl::LifetimeExtendedTemporary: + case Decl::RequiresExprBody: return false; // These indirectly derive from Redeclarable<T> but are not actually diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 19e7ebe03a1fd..8e8b04451fb15 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6576,6 +6576,17 @@ void TypeLocReader::VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) { void TypeLocReader::VisitAutoTypeLoc(AutoTypeLoc TL) { TL.setNameLoc(readSourceLocation()); + if (Reader.readBool()) { + TL.setNestedNameSpecifierLoc(ReadNestedNameSpecifierLoc()); + TL.setTemplateKWLoc(readSourceLocation()); + TL.setConceptNameLoc(readSourceLocation()); + TL.setFoundDecl(Reader.readDeclAs<NamedDecl>()); + TL.setLAngleLoc(readSourceLocation()); + TL.setRAngleLoc(readSourceLocation()); + for (unsigned i = 0, e = TL.getNumArgs(); i != e; ++i) + TL.setArgLocInfo(i, Reader.readTemplateArgumentLocInfo( + TL.getTypePtr()->getArg(i).getKind())); + } } void TypeLocReader::VisitDeducedTemplateSpecializationTypeLoc( diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 96a7d5ae0a31c..093b69ab19d03 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -375,6 +375,7 @@ namespace clang { void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D); DeclID VisitTemplateDecl(TemplateDecl *D); void VisitConceptDecl(ConceptDecl *D); + void VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D); RedeclarableResult VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D); void VisitClassTemplateDecl(ClassTemplateDecl *D); void VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D); @@ -2037,6 +2038,9 @@ void ASTDeclReader::VisitConceptDecl(ConceptDecl *D) { mergeMergeable(D); } +void ASTDeclReader::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) { +} + ASTDeclReader::RedeclarableResult ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { RedeclarableResult Redecl = VisitRedeclarable(D); @@ -2313,12 +2317,12 @@ void ASTDeclReader::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { D->setDeclaredWithTypename(Record.readInt()); - if (Record.readInt()) { + if (Record.readBool()) { NestedNameSpecifierLoc NNS = Record.readNestedNameSpecifierLoc(); DeclarationNameInfo DN = Record.readDeclarationNameInfo(); - ConceptDecl *NamedConcept = cast<ConceptDecl>(Record.readDecl()); + ConceptDecl *NamedConcept = Record.readDeclAs<ConceptDecl>(); const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr; - if (Record.readInt()) + if (Record.readBool()) ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); Expr *ImmediatelyDeclaredConstraint = Record.readExpr(); D->setTypeConstraint(NNS, DN, /*FoundDecl=*/nullptr, NamedConcept, @@ -2336,6 +2340,8 @@ void ASTDeclReader::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { // TemplateParmPosition. D->setDepth(Record.readInt()); D->setPosition(Record.readInt()); + if (D->hasPlaceholderTypeConstraint()) + D->setPlaceholderTypeConstraint(Record.readExpr()); if (D->isExpandedParameterPack()) { auto TypesAndInfos = D->getTrailingObjects<std::pair<QualType, TypeSourceInfo *>>(); @@ -3819,13 +3825,19 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { HasTypeConstraint); break; } - case DECL_NON_TYPE_TEMPLATE_PARM: - D = NonTypeTemplateParmDecl::CreateDeserialized(Context, ID); + case DECL_NON_TYPE_TEMPLATE_PARM: { + bool HasTypeConstraint = Record.readInt(); + D = NonTypeTemplateParmDecl::CreateDeserialized(Context, ID, + HasTypeConstraint); break; - case DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK: + } + case DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK: { + bool HasTypeConstraint = Record.readInt(); D = NonTypeTemplateParmDecl::CreateDeserialized(Context, ID, - Record.readInt()); + Record.readInt(), + HasTypeConstraint); break; + } case DECL_TEMPLATE_TEMPLATE_PARM: D = TemplateTemplateParmDecl::CreateDeserialized(Context, ID); break; @@ -3839,6 +3851,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { case DECL_CONCEPT: D = ConceptDecl::CreateDeserialized(Context, ID); break; + case DECL_REQUIRES_EXPR_BODY: + D = RequiresExprBodyDecl::CreateDeserialized(Context, ID); + break; case DECL_STATIC_ASSERT: D = StaticAssertDecl::CreateDeserialized(Context, ID); break; diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index f558c26b5f1e8..5dd0ef9d43c3e 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -724,27 +724,15 @@ void ASTStmtReader::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) { E->setRParenLoc(readSourceLocation()); } -void ASTStmtReader::VisitConceptSpecializationExpr( - ConceptSpecializationExpr *E) { - VisitExpr(E); - unsigned NumTemplateArgs = Record.readInt(); - E->NestedNameSpec = Record.readNestedNameSpecifierLoc(); - E->TemplateKWLoc = Record.readSourceLocation(); - E->ConceptName = Record.readDeclarationNameInfo(); - E->NamedConcept = readDeclAs<ConceptDecl>(); - E->ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); - llvm::SmallVector<TemplateArgument, 4> Args; - for (unsigned I = 0; I < NumTemplateArgs; ++I) - Args.push_back(Record.readTemplateArgument()); - E->setTemplateArguments(Args); +static ConstraintSatisfaction +readConstraintSatisfaction(ASTRecordReader &Record) { ConstraintSatisfaction Satisfaction; Satisfaction.IsSatisfied = Record.readInt(); if (!Satisfaction.IsSatisfied) { unsigned NumDetailRecords = Record.readInt(); for (unsigned i = 0; i != NumDetailRecords; ++i) { Expr *ConstraintExpr = Record.readExpr(); - bool IsDiagnostic = Record.readInt(); - if (IsDiagnostic) { + if (bool IsDiagnostic = Record.readInt()) { SourceLocation DiagLocation = Record.readSourceLocation(); std::string DiagMessage = Record.readString(); Satisfaction.Details.emplace_back( @@ -755,8 +743,137 @@ void ASTStmtReader::VisitConceptSpecializationExpr( Satisfaction.Details.emplace_back(ConstraintExpr, Record.readExpr()); } } - E->Satisfaction = ASTConstraintSatisfaction::Create(Record.getContext(), - Satisfaction); + return Satisfaction; +} + +void ASTStmtReader::VisitConceptSpecializationExpr( + ConceptSpecializationExpr *E) { + VisitExpr(E); + unsigned NumTemplateArgs = Record.readInt(); + E->NestedNameSpec = Record.readNestedNameSpecifierLoc(); + E->TemplateKWLoc = Record.readSourceLocation(); + E->ConceptName = Record.readDeclarationNameInfo(); + E->NamedConcept = readDeclAs<ConceptDecl>(); + E->ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); + llvm::SmallVector<TemplateArgument, 4> Args; + for (unsigned I = 0; I < NumTemplateArgs; ++I) + Args.push_back(Record.readTemplateArgument()); + E->setTemplateArguments(Args); + E->Satisfaction = E->isValueDependent() ? nullptr : + ASTConstraintSatisfaction::Create(Record.getContext(), + readConstraintSatisfaction(Record)); +} + +static concepts::Requirement::SubstitutionDiagnostic * +readSubstitutionDiagnostic(ASTRecordReader &Record) { + std::string SubstitutedEntity = Record.readString(); + SourceLocation DiagLoc = Record.readSourceLocation(); + std::string DiagMessage = Record.readString(); + return new (Record.getContext()) + concepts::Requirement::SubstitutionDiagnostic{SubstitutedEntity, DiagLoc, + DiagMessage}; +} + +void ASTStmtReader::VisitRequiresExpr(RequiresExpr *E) { + VisitExpr(E); + unsigned NumLocalParameters = Record.readInt(); + unsigned NumRequirements = Record.readInt(); + E->RequiresExprBits.RequiresKWLoc = Record.readSourceLocation(); + E->RequiresExprBits.IsSatisfied = Record.readInt(); + E->Body = Record.readDeclAs<RequiresExprBodyDecl>(); + llvm::SmallVector<ParmVarDecl *, 4> LocalParameters; + for (unsigned i = 0; i < NumLocalParameters; ++i) + LocalParameters.push_back(cast<ParmVarDecl>(Record.readDecl())); + std::copy(LocalParameters.begin(), LocalParameters.end(), + E->getTrailingObjects<ParmVarDecl *>()); + llvm::SmallVector<concepts::Requirement *, 4> Requirements; + for (unsigned i = 0; i < NumRequirements; ++i) { + auto RK = + static_cast<concepts::Requirement::RequirementKind>(Record.readInt()); + concepts::Requirement *R = nullptr; + switch (RK) { + case concepts::Requirement::RK_Type: { + auto Status = + static_cast<concepts::TypeRequirement::SatisfactionStatus>( + Record.readInt()); + if (Status == concepts::TypeRequirement::SS_SubstitutionFailure) + R = new (Record.getContext()) + concepts::TypeRequirement(readSubstitutionDiagnostic(Record)); + else + R = new (Record.getContext()) + concepts::TypeRequirement(Record.readTypeSourceInfo()); + } break; + case concepts::Requirement::RK_Simple: + case concepts::Requirement::RK_Compound: { + auto Status = + static_cast<concepts::ExprRequirement::SatisfactionStatus>( + Record.readInt()); + llvm::PointerUnion<concepts::Requirement::SubstitutionDiagnostic *, + Expr *> E; + if (Status == concepts::ExprRequirement::SS_ExprSubstitutionFailure) { + E = readSubstitutionDiagnostic(Record); + } else + E = Record.readExpr(); + + llvm::Optional<concepts::ExprRequirement::ReturnTypeRequirement> Req; + ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr; + SourceLocation NoexceptLoc; + if (RK == concepts::Requirement::RK_Simple) { + Req.emplace(); + } else { + NoexceptLoc = Record.readSourceLocation(); + switch (auto returnTypeRequirementKind = Record.readInt()) { + case 0: + // No return type requirement. + Req.emplace(); + break; + case 1: { + // type-constraint + TemplateParameterList *TPL = Record.readTemplateParameterList(); + if (Status >= + concepts::ExprRequirement::SS_ConstraintsNotSatisfied) + SubstitutedConstraintExpr = + cast<ConceptSpecializationExpr>(Record.readExpr()); + Req.emplace(TPL); + } break; + case 2: + // Substitution failure + Req.emplace(readSubstitutionDiagnostic(Record)); + break; + } + } + if (Expr *Ex = E.dyn_cast<Expr *>()) + R = new (Record.getContext()) concepts::ExprRequirement( + Ex, RK == concepts::Requirement::RK_Simple, NoexceptLoc, + std::move(*Req), Status, SubstitutedConstraintExpr); + else + R = new (Record.getContext()) concepts::ExprRequirement( + E.get<concepts::Requirement::SubstitutionDiagnostic *>(), + RK == concepts::Requirement::RK_Simple, NoexceptLoc, + std::move(*Req)); + } break; + case concepts::Requirement::RK_Nested: { + if (bool IsSubstitutionDiagnostic = Record.readInt()) { + R = new (Record.getContext()) concepts::NestedRequirement( + readSubstitutionDiagnostic(Record)); + break; + } + Expr *E = Record.readExpr(); + if (E->isInstantiationDependent()) + R = new (Record.getContext()) concepts::NestedRequirement(E); + else + R = new (Record.getContext()) + concepts::NestedRequirement(Record.getContext(), E, + readConstraintSatisfaction(Record)); + } break; + } + if (!R) + continue; + Requirements.push_back(R); + } + std::copy(Requirements.begin(), Requirements.end(), + E->getTrailingObjects<concepts::Requirement *>()); + E->RBraceLoc = Record.readSourceLocation(); } void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { @@ -3566,11 +3683,18 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) DependentCoawaitExpr(Empty); break; - case EXPR_CONCEPT_SPECIALIZATION: + case EXPR_CONCEPT_SPECIALIZATION: { unsigned numTemplateArgs = Record[ASTStmtReader::NumExprFields]; S = ConceptSpecializationExpr::Create(Context, Empty, numTemplateArgs); break; - + } + + case EXPR_REQUIRES: + unsigned numLocalParameters = Record[ASTStmtReader::NumExprFields]; + unsigned numRequirement = Record[ASTStmtReader::NumExprFields + 1]; + S = RequiresExpr::Create(Context, Empty, numLocalParameters, + numRequirement); + break; } // We hit a STMT_STOP, so we're done with this expression. diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 6eba48a1abe97..252853aad1f86 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -349,6 +349,18 @@ void TypeLocWriter::VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) { void TypeLocWriter::VisitAutoTypeLoc(AutoTypeLoc TL) { Record.AddSourceLocation(TL.getNameLoc()); + Record.push_back(TL.isConstrained()); + if (TL.isConstrained()) { + Record.AddNestedNameSpecifierLoc(TL.getNestedNameSpecifierLoc()); + Record.AddSourceLocation(TL.getTemplateKWLoc()); + Record.AddSourceLocation(TL.getConceptNameLoc()); + Record.AddDeclRef(TL.getFoundDecl()); + Record.AddSourceLocation(TL.getLAngleLoc()); + Record.AddSourceLocation(TL.getRAngleLoc()); + for (unsigned I = 0; I < TL.getNumArgs(); ++I) + Record.AddTemplateArgumentLocInfo(TL.getTypePtr()->getArg(I).getKind(), + TL.getArgLocInfo(I)); + } } void TypeLocWriter::VisitDeducedTemplateSpecializationTypeLoc( @@ -885,6 +897,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(DECL_NON_TYPE_TEMPLATE_PARM); RECORD(DECL_TEMPLATE_TEMPLATE_PARM); RECORD(DECL_CONCEPT); + RECORD(DECL_REQUIRES_EXPR_BODY); RECORD(DECL_TYPE_ALIAS_TEMPLATE); RECORD(DECL_STATIC_ASSERT); RECORD(DECL_CXX_BASE_SPECIFIERS); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index b2a8c118d4011..472136d99a131 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -104,6 +104,7 @@ namespace clang { void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D); void VisitTemplateDecl(TemplateDecl *D); void VisitConceptDecl(ConceptDecl *D); + void VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D); void VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D); void VisitClassTemplateDecl(ClassTemplateDecl *D); void VisitVarTemplateDecl(VarTemplateDecl *D); @@ -1481,6 +1482,10 @@ void ASTDeclWriter::VisitConceptDecl(ConceptDecl *D) { Code = serialization::DECL_CONCEPT; } +void ASTDeclWriter::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) { + Code = serialization::DECL_REQUIRES_EXPR_BODY; +} + void ASTDeclWriter::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { VisitRedeclarable(D); @@ -1670,6 +1675,8 @@ void ASTDeclWriter::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { // For an expanded parameter pack, record the number of expansion types here // so that it's easier for deserialization to allocate the right amount of // memory. + Expr *TypeConstraint = D->getPlaceholderTypeConstraint(); + Record.push_back(!!TypeConstraint); if (D->isExpandedParameterPack()) Record.push_back(D->getNumExpansionTypes()); @@ -1677,6 +1684,8 @@ void ASTDeclWriter::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { // TemplateParmPosition. Record.push_back(D->getDepth()); Record.push_back(D->getPosition()); + if (TypeConstraint) + Record.AddStmt(TypeConstraint); if (D->isExpandedParameterPack()) { for (unsigned I = 0, N = D->getNumExpansionTypes(); I != N; ++I) { diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 9231f3b2b9ba2..1b118c257a4cc 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Serialization/ASTRecordWriter.h" +#include "clang/Sema/DeclSpec.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -388,19 +389,9 @@ void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) { Code = serialization::EXPR_DEPENDENT_COAWAIT; } -void ASTStmtWriter::VisitConceptSpecializationExpr( - ConceptSpecializationExpr *E) { - VisitExpr(E); - ArrayRef<TemplateArgument> TemplateArgs = E->getTemplateArguments(); - Record.push_back(TemplateArgs.size()); - Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc()); - Record.AddSourceLocation(E->getTemplateKWLoc()); - Record.AddDeclarationNameInfo(E->getConceptNameInfo()); - Record.AddDeclRef(E->getNamedConcept()); - Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten()); - for (const TemplateArgument &Arg : TemplateArgs) - Record.AddTemplateArgument(Arg); - const ASTConstraintSatisfaction &Satisfaction = E->getSatisfaction(); +static void +addConstraintSatisfaction(ASTRecordWriter &Record, + const ASTConstraintSatisfaction &Satisfaction) { Record.push_back(Satisfaction.IsSatisfied); if (!Satisfaction.IsSatisfied) { Record.push_back(Satisfaction.NumRecords); @@ -418,10 +409,98 @@ void ASTStmtWriter::VisitConceptSpecializationExpr( } } } +} + +static void +addSubstitutionDiagnostic( + ASTRecordWriter &Record, + const concepts::Requirement::SubstitutionDiagnostic *D) { + Record.AddString(D->SubstitutedEntity); + Record.AddSourceLocation(D->DiagLoc); + Record.AddString(D->DiagMessage); +} + +void ASTStmtWriter::VisitConceptSpecializationExpr( + ConceptSpecializationExpr *E) { + VisitExpr(E); + ArrayRef<TemplateArgument> TemplateArgs = E->getTemplateArguments(); + Record.push_back(TemplateArgs.size()); + Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc()); + Record.AddSourceLocation(E->getTemplateKWLoc()); + Record.AddDeclarationNameInfo(E->getConceptNameInfo()); + Record.AddDeclRef(E->getNamedConcept()); + Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten()); + for (const TemplateArgument &Arg : TemplateArgs) + Record.AddTemplateArgument(Arg); + if (!E->isValueDependent()) + addConstraintSatisfaction(Record, E->getSatisfaction()); Code = serialization::EXPR_CONCEPT_SPECIALIZATION; } +void ASTStmtWriter::VisitRequiresExpr(RequiresExpr *E) { + VisitExpr(E); + Record.push_back(E->getLocalParameters().size()); + Record.push_back(E->getRequirements().size()); + Record.AddSourceLocation(E->RequiresExprBits.RequiresKWLoc); + Record.push_back(E->RequiresExprBits.IsSatisfied); + Record.AddDeclRef(E->getBody()); + for (ParmVarDecl *P : E->getLocalParameters()) + Record.AddDeclRef(P); + for (concepts::Requirement *R : E->getRequirements()) { + if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(R)) { + Record.push_back(concepts::Requirement::RK_Type); + Record.push_back(TypeReq->Status); + if (TypeReq->Status == concepts::TypeRequirement::SS_SubstitutionFailure) + addSubstitutionDiagnostic(Record, TypeReq->getSubstitutionDiagnostic()); + else + Record.AddTypeSourceInfo(TypeReq->getType()); + } else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(R)) { + Record.push_back(ExprReq->getKind()); + Record.push_back(ExprReq->Status); + if (ExprReq->isExprSubstitutionFailure()) { + addSubstitutionDiagnostic(Record, + ExprReq->Value.get<concepts::Requirement::SubstitutionDiagnostic *>()); + } else + Record.AddStmt(ExprReq->Value.get<Expr *>()); + if (ExprReq->getKind() == concepts::Requirement::RK_Compound) { + Record.AddSourceLocation(ExprReq->NoexceptLoc); + const auto &RetReq = ExprReq->getReturnTypeRequirement(); + if (RetReq.isSubstitutionFailure()) { + Record.push_back(2); + addSubstitutionDiagnostic(Record, RetReq.getSubstitutionDiagnostic()); + } else if (RetReq.isTypeConstraint()) { + Record.push_back(1); + Record.AddTemplateParameterList( + RetReq.getTypeConstraintTemplateParameterList()); + if (ExprReq->Status >= + concepts::ExprRequirement::SS_ConstraintsNotSatisfied) + Record.AddStmt( + ExprReq->getReturnTypeRequirementSubstitutedConstraintExpr()); + } else { + assert(RetReq.isEmpty()); + Record.push_back(0); + } + } + } else { + auto *NestedReq = cast<concepts::NestedRequirement>(R); + Record.push_back(concepts::Requirement::RK_Nested); + Record.push_back(NestedReq->isSubstitutionFailure()); + if (NestedReq->isSubstitutionFailure()){ + addSubstitutionDiagnostic(Record, + NestedReq->getSubstitutionDiagnostic()); + } else { + Record.AddStmt(NestedReq->Value.get<Expr *>()); + if (!NestedReq->isDependent()) + addConstraintSatisfaction(Record, *NestedReq->Satisfaction); + } + } + } + Record.AddSourceLocation(E->getEndLoc()); + + Code = serialization::EXPR_REQUIRES; +} + void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) { VisitStmt(S); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index f917a4c8637b6..b542cf2c03038 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1386,6 +1386,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::AsTypeExprClass: case Stmt::ConceptSpecializationExprClass: case Stmt::CXXRewrittenBinaryOperatorClass: + case Stmt::RequiresExprClass: // Fall through. // Cases we intentionally don't evaluate, since they don't need |
