diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h --- a/clang/include/clang/AST/ASTConcept.h +++ b/clang/include/clang/AST/ASTConcept.h @@ -22,6 +22,9 @@ namespace clang { class ConceptDecl; +namespace concepts { +struct SubstitutionDiagnostic; +} // namespace concepts /// The result of a constraint satisfaction check, containing the necessary /// information to diagnose an unsatisfied constraint. @@ -40,8 +43,7 @@ ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs.begin(), TemplateArgs.end()) { } - using SubstitutionDiagnostic = std::pair; - using Detail = llvm::PointerUnion; + using Detail = llvm::PointerUnion; bool IsSatisfied = false; bool ContainsErrors = false; @@ -66,9 +68,7 @@ /// substituted into them, or a diagnostic if substitution resulted in /// an invalid expression. using UnsatisfiedConstraintRecord = - std::pair *>>; + std::pair; /// \brief The result of a constraint satisfaction check, containing the /// necessary information to diagnose an unsatisfied constraint. diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h --- a/clang/include/clang/AST/ExprConcepts.h +++ b/clang/include/clang/AST/ExprConcepts.h @@ -29,6 +29,10 @@ #include namespace clang { +namespace sema { +class TemplateDeductionInfo; +} + class ASTStmtReader; class ASTStmtWriter; @@ -42,7 +46,6 @@ friend class ASTStmtReader; public: - using SubstitutionDiagnostic = std::pair; protected: /// \brief The Implicit Concept Specialization Decl, which holds the template @@ -53,6 +56,7 @@ /// given arguments. If this expression is value dependent, this is to be /// ignored. ASTConstraintSatisfaction *Satisfaction; + bool ArgsHasSubstitutionFailure = false; ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, @@ -60,7 +64,8 @@ NamedDecl *FoundDecl, ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, ImplicitConceptSpecializationDecl *SpecDecl, - const ConstraintSatisfaction *Satisfaction); + const ConstraintSatisfaction *Satisfaction, + bool ArgsHasSubstitutionFailure = false); ConceptSpecializationExpr(const ASTContext &C, ConceptDecl *NamedConcept, ImplicitConceptSpecializationDecl *SpecDecl, @@ -84,10 +89,21 @@ const ConstraintSatisfaction *Satisfaction, bool Dependent, bool ContainsUnexpandedParameterPack); + static ConceptSpecializationExpr * + CreateSubstitutionFailure(const ASTContext &C, NestedNameSpecifierLoc NNS, + SourceLocation TemplateKWLoc, + DeclarationNameInfo ConceptNameInfo, + NamedDecl *FoundDecl, ConceptDecl *NamedConcept, + const ConstraintSatisfaction *Satisfaction); + ArrayRef getTemplateArguments() const { return SpecDecl->getTemplateArguments(); } + bool hasSubstitutionFailureInArgs() const { + return ArgsHasSubstitutionFailure; + } + const ImplicitConceptSpecializationDecl *getSpecializationDecl() const { assert(SpecDecl && "Template Argument Decl not initialized"); return SpecDecl; @@ -139,6 +155,15 @@ }; namespace concepts { + // todo: add doc ? +struct SubstitutionDiagnostic { + StringRef SubstitutedEntity; + // FIXME: Store diagnostics semantically and not as prerendered strings. + // Fixing this probably requires serialization of PartialDiagnostic + // objects. + SourceLocation DiagLoc; + StringRef DiagMessage; +}; /// \brief A static requirement that can be used in a requires-expression to /// check properties of types and expression. @@ -154,14 +179,6 @@ bool ContainsUnexpandedParameterPack : 1; bool Satisfied : 1; public: - struct SubstitutionDiagnostic { - StringRef SubstitutedEntity; - // FIXME: Store diagnostics semantically and not as prerendered strings. - // Fixing this probably requires serialization of PartialDiagnostic - // objects. - SourceLocation DiagLoc; - StringRef DiagMessage; - }; Requirement(RequirementKind Kind, bool IsDependent, bool ContainsUnexpandedParameterPack, bool IsSatisfied = true) : diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8448,19 +8448,16 @@ BuildExprRequirement( Expr *E, bool IsSatisfied, SourceLocation NoexceptLoc, concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement); - concepts::ExprRequirement * - BuildExprRequirement( - concepts::Requirement::SubstitutionDiagnostic *ExprSubstDiag, - bool IsSatisfied, SourceLocation NoexceptLoc, + concepts::ExprRequirement *BuildExprRequirement( + concepts::SubstitutionDiagnostic *ExprSubstDiag, bool IsSatisfied, + SourceLocation NoexceptLoc, concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement); concepts::TypeRequirement *BuildTypeRequirement(TypeSourceInfo *Type); concepts::TypeRequirement * - BuildTypeRequirement( - concepts::Requirement::SubstitutionDiagnostic *SubstDiag); + BuildTypeRequirement(concepts::SubstitutionDiagnostic *SubstDiag); concepts::NestedRequirement *BuildNestedRequirement(Expr *E); concepts::NestedRequirement * - BuildNestedRequirement( - concepts::Requirement::SubstitutionDiagnostic *SubstDiag); + BuildNestedRequirement(concepts::SubstitutionDiagnostic *SubstDiag); ExprResult ActOnRequiresExpr(SourceLocation RequiresKWLoc, RequiresExprBodyDecl *Body, ArrayRef LocalParameters, diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp --- a/clang/lib/AST/ASTConcept.cpp +++ b/clang/lib/AST/ASTConcept.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ASTConcept.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/TemplateBase.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" @@ -33,12 +34,13 @@ Detail.second.get())}; else { auto &SubstitutionDiagnostic = - *Detail.second.get *>(); - unsigned MessageSize = SubstitutionDiagnostic.second.size(); + *Detail.second.get(); + unsigned MessageSize = SubstitutionDiagnostic.DiagMessage.size(); char *Mem = new (C) char[MessageSize]; - memcpy(Mem, SubstitutionDiagnostic.second.data(), MessageSize); - auto *NewSubstDiag = new (C) std::pair( - SubstitutionDiagnostic.first, StringRef(Mem, MessageSize)); + memcpy(Mem, SubstitutionDiagnostic.DiagMessage.data(), MessageSize); + auto *NewSubstDiag = new (C) concepts::SubstitutionDiagnostic{ + /*todo: copy me*/ SubstitutionDiagnostic.SubstitutedEntity, + SubstitutionDiagnostic.DiagLoc, StringRef(Mem, MessageSize)}; new (getTrailingObjects() + I) UnsatisfiedConstraintRecord{Detail.first, UnsatisfiedConstraintRecord::second_type( diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -844,12 +844,13 @@ auto TA = TemplateArgumentDependence::None; const auto InterestingDeps = TemplateArgumentDependence::Instantiation | TemplateArgumentDependence::UnexpandedPack; - for (const TemplateArgumentLoc &ArgLoc : - E->getTemplateArgsAsWritten()->arguments()) { - TA |= ArgLoc.getArgument().getDependence() & InterestingDeps; - if (TA == InterestingDeps) - break; - } + if (E->getTemplateArgsAsWritten()) + for (const TemplateArgumentLoc &ArgLoc : + E->getTemplateArgsAsWritten()->arguments()) { + TA |= ArgLoc.getArgument().getDependence() & InterestingDeps; + if (TA == InterestingDeps) + break; + } ExprDependence D = ValueDependent ? ExprDependence::Value : ExprDependence::None; diff --git a/clang/lib/AST/ExprConcepts.cpp b/clang/lib/AST/ExprConcepts.cpp --- a/clang/lib/AST/ExprConcepts.cpp +++ b/clang/lib/AST/ExprConcepts.cpp @@ -23,6 +23,8 @@ #include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/TemplateDeduction.h" #include "llvm/Support/TrailingObjects.h" #include #include @@ -36,14 +38,15 @@ NamedDecl *FoundDecl, ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, ImplicitConceptSpecializationDecl *SpecDecl, - const ConstraintSatisfaction *Satisfaction) + const ConstraintSatisfaction *Satisfaction, bool ArgsHasSubstitutionFailure) : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_PRValue, OK_Ordinary), ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept, ArgsAsWritten), SpecDecl(SpecDecl), Satisfaction(Satisfaction ? ASTConstraintSatisfaction::Create(C, *Satisfaction) - : nullptr) { + : nullptr), + ArgsHasSubstitutionFailure(ArgsHasSubstitutionFailure) { setDependence(computeDependence(this, /*ValueDependent=*/!Satisfaction)); // Currently guaranteed by the fact concepts can only be at namespace-scope. @@ -53,6 +56,13 @@ ->containsUnexpandedParameterPack())); assert((!isValueDependent() || isInstantiationDependent()) && "should not be value-dependent"); + if (ArgsHasSubstitutionFailure) { + assert(Satisfaction); + assert(!Satisfaction->IsSatisfied); + // assert(Satisfaction->ContainsErrors); + assert(!ArgsAsWritten); + assert(!SpecDecl); + } } ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty) @@ -103,6 +113,17 @@ Dependent, ContainsUnexpandedParameterPack); } +ConceptSpecializationExpr *ConceptSpecializationExpr::CreateSubstitutionFailure( + const ASTContext &C, NestedNameSpecifierLoc NNS, + SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo, + NamedDecl *FoundDecl, ConceptDecl *NamedConcept, + const ConstraintSatisfaction *Satisfaction) { + return new (C) ConceptSpecializationExpr( + C, NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept, + /*ArgsAsWritten=*/nullptr, /*SpecDecl=*/nullptr, Satisfaction, + /*ArgsHasSubstitutionFailure=*/true); +} + const TypeConstraint * concepts::ExprRequirement::ReturnTypeRequirement::getTypeConstraint() const { assert(isTypeConstraint()); diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2467,9 +2467,12 @@ if (E->getTemplateKWLoc().isValid()) OS << "template "; OS << E->getFoundDecl()->getName(); - printTemplateArgumentList(OS, E->getTemplateArgsAsWritten()->arguments(), - Policy, - E->getNamedConcept()->getTemplateParameters()); + if (E->hasSubstitutionFailureInArgs()) + OS << "<>"; + else + printTemplateArgumentList(OS, E->getTemplateArgsAsWritten()->arguments(), + Policy, + E->getNamedConcept()->getTemplateParameters()); } void StmtPrinter::VisitRequiresExpr(RequiresExpr *E) { diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -223,10 +223,9 @@ char *Mem = new (S.Context) char[MessageSize]; memcpy(Mem, DiagString.c_str(), MessageSize); Satisfaction.Details.emplace_back( - ConstraintExpr, - new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{ - SubstitutedAtomicExpr.get()->getBeginLoc(), - StringRef(Mem, MessageSize)}); + ConstraintExpr, new (S.Context) concepts::SubstitutionDiagnostic{ + "", SubstitutedAtomicExpr.get()->getBeginLoc(), + StringRef(Mem, MessageSize)}); return SubstitutedAtomicExpr; } @@ -314,9 +313,9 @@ char *Mem = new (S.Context) char[MessageSize]; memcpy(Mem, DiagString.c_str(), MessageSize); Satisfaction.Details.emplace_back( - AtomicExpr, - new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{ - SubstDiag.first, StringRef(Mem, MessageSize)}); + AtomicExpr, new (S.Context) concepts::SubstitutionDiagnostic{ + "todo: fill substituted entity", + SubstDiag.first, StringRef(Mem, MessageSize)}); Satisfaction.IsSatisfied = false; return ExprEmpty(); } @@ -858,7 +857,7 @@ concepts::NestedRequirement *Req, bool First) { if (Req->isSubstitutionFailure()) { - concepts::Requirement::SubstitutionDiagnostic *SubstDiag = + concepts::SubstitutionDiagnostic *SubstDiag = Req->getSubstitutionDiagnostic(); if (!SubstDiag->DiagMessage.empty()) S.Diag(SubstDiag->DiagLoc, @@ -941,6 +940,19 @@ break; } } else if (auto *CSE = dyn_cast(SubstExpr)) { + if (CSE->hasSubstitutionFailureInArgs()) { + for (const auto &Diags : CSE->getSatisfaction()) { + auto *SubstDiag = + Diags.second.get(); + S.Diag(SubstDiag->DiagLoc, + // todo: create another diag for concept specialisation. this one + // is not needed anymore. + diag::note_nested_requirement_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + } + return; + } if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) { S.Diag( CSE->getSourceRange().getBegin(), @@ -982,8 +994,8 @@ const llvm::PointerUnion &Record, bool First = true) { if (auto *Diag = Record.template dyn_cast()){ - S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed) - << Diag->second; + S.Diag(Diag->DiagLoc, diag::note_substituted_constraint_expr_is_ill_formed) + << Diag->DiagMessage; return; } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8989,10 +8989,9 @@ SubstitutedConstraintExpr); } -concepts::ExprRequirement * -Sema::BuildExprRequirement( - concepts::Requirement::SubstitutionDiagnostic *ExprSubstitutionDiagnostic, - bool IsSimple, SourceLocation NoexceptLoc, +concepts::ExprRequirement *Sema::BuildExprRequirement( + concepts::SubstitutionDiagnostic *ExprSubstitutionDiagnostic, bool IsSimple, + SourceLocation NoexceptLoc, concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) { return new (Context) concepts::ExprRequirement(ExprSubstitutionDiagnostic, IsSimple, NoexceptLoc, @@ -9005,8 +9004,7 @@ } concepts::TypeRequirement * -Sema::BuildTypeRequirement( - concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { +Sema::BuildTypeRequirement(concepts::SubstitutionDiagnostic *SubstDiag) { return new (Context) concepts::TypeRequirement(SubstDiag); } @@ -9026,8 +9024,7 @@ } concepts::NestedRequirement * -Sema::BuildNestedRequirement( - concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { +Sema::BuildNestedRequirement(concepts::SubstitutionDiagnostic *SubstDiag) { return new (Context) concepts::NestedRequirement(SubstDiag); } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -2151,8 +2151,8 @@ getPackIndex(Pack), Arg, TL.getNameLoc()); } -template -static concepts::Requirement::SubstitutionDiagnostic * +template +static concepts::SubstitutionDiagnostic * createSubstDiag(Sema &S, TemplateDeductionInfo &Info, EntityPrinter Printer) { SmallString<128> Message; SourceLocation ErrorLoc; @@ -2172,7 +2172,7 @@ Printer(OS); char *EntityBuf = new (S.Context) char[Entity.size()]; std::copy(Entity.begin(), Entity.end(), EntityBuf); - return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{ + return new (S.Context) concepts::SubstitutionDiagnostic{ StringRef(EntityBuf, Entity.size()), ErrorLoc, StringRef(MessageBuf, Message.size())}; } @@ -2228,9 +2228,9 @@ 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( + createSubstDiag(SemaRef, Info, [&](llvm::raw_ostream &OS) { + Req->getType()->getType().print(OS, SemaRef.getPrintingPolicy()); })); return RebuildTypeRequirement(TransType); } @@ -2242,8 +2242,7 @@ Sema::SFINAETrap Trap(SemaRef); - llvm::PointerUnion - TransExpr; + llvm::PointerUnion TransExpr; if (Req->isExprSubstitutionFailure()) TransExpr = Req->getExprSubstitutionDiagnostic(); else { @@ -2258,9 +2257,10 @@ TransExprRes.get()->hasPlaceholderType()) TransExprRes = SemaRef.CheckPlaceholderExpr(TransExprRes.get()); if (TransExprRes.isInvalid() || Trap.hasErrorOccurred()) - TransExpr = createSubstDiag(SemaRef, Info, [&](llvm::raw_ostream &OS) { - E->printPretty(OS, nullptr, SemaRef.getPrintingPolicy()); - }); + TransExpr = + createSubstDiag(SemaRef, Info, [&](llvm::raw_ostream &OS) { + E->printPretty(OS, nullptr, SemaRef.getPrintingPolicy()); + }); else TransExpr = TransExprRes.get(); } @@ -2281,10 +2281,11 @@ 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()); + TransRetReq.emplace( + createSubstDiag(SemaRef, Info, [&](llvm::raw_ostream &OS) { + RetReq.getTypeConstraint() + ->getImmediatelyDeclaredConstraint() + ->printPretty(OS, nullptr, SemaRef.getPrintingPolicy()); })); else { TPLInst.Clear(); @@ -2296,8 +2297,8 @@ return RebuildExprRequirement(E, Req->isSimple(), Req->getNoexceptLoc(), std::move(*TransRetReq)); return RebuildExprRequirement( - TransExpr.get(), - Req->isSimple(), Req->getNoexceptLoc(), std::move(*TransRetReq)); + TransExpr.get(), Req->isSimple(), + Req->getNoexceptLoc(), std::move(*TransRetReq)); } concepts::NestedRequirement * @@ -2349,10 +2350,10 @@ "but did not produce a SFINAE error"); } if (TransConstraint.isInvalid() || Trap.hasErrorOccurred()) - return RebuildNestedRequirement(createSubstDiag(SemaRef, Info, - [&] (llvm::raw_ostream& OS) { - Req->getConstraintExpr()->printPretty(OS, nullptr, - SemaRef.getPrintingPolicy()); + return RebuildNestedRequirement( + createSubstDiag(SemaRef, Info, [&](llvm::raw_ostream &OS) { + Req->getConstraintExpr()->printPretty(OS, nullptr, + SemaRef.getPrintingPolicy()); })); } if (TransConstraint.get()->isInstantiationDependent()) diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -37,6 +37,7 @@ #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Sema/SemaInternal.h" +#include "clang/Sema/TemplateDeduction.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/ErrorHandling.h" #include @@ -3477,6 +3478,23 @@ return Result; } + ExprResult RebuildConceptSpecializationSubstitutionFailure( + ConceptSpecializationExpr *E, + concepts::SubstitutionDiagnostic *SubstDiags) { + ConstraintSatisfaction Satisfaction; + Satisfaction.IsSatisfied = false; + Satisfaction.ContainsErrors = false; + Satisfaction.Details.emplace_back(E, SubstDiags); + CXXScopeSpec SS; + SS.Adopt(E->getNestedNameSpecifierLoc()); + return ConceptSpecializationExpr::CreateSubstitutionFailure( + SemaRef.Context, + SS.isSet() ? SS.getWithLocInContext(SemaRef.Context) + : NestedNameSpecifierLoc{}, + E->getTemplateKWLoc(), E->getConceptNameInfo(), E->getFoundDecl(), + E->getNamedConcept(), &Satisfaction); + } + /// \brief Build a new requires expression. /// /// By default, performs semantic analysis to build the new expression. @@ -3491,8 +3509,7 @@ } concepts::TypeRequirement * - RebuildTypeRequirement( - concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + RebuildTypeRequirement(concepts::SubstitutionDiagnostic *SubstDiag) { return SemaRef.BuildTypeRequirement(SubstDiag); } @@ -3501,10 +3518,9 @@ } concepts::ExprRequirement * - RebuildExprRequirement( - concepts::Requirement::SubstitutionDiagnostic *SubstDiag, bool IsSimple, - SourceLocation NoexceptLoc, - concepts::ExprRequirement::ReturnTypeRequirement Ret) { + RebuildExprRequirement(concepts::SubstitutionDiagnostic *SubstDiag, + bool IsSimple, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement Ret) { return SemaRef.BuildExprRequirement(SubstDiag, IsSimple, NoexceptLoc, std::move(Ret)); } @@ -3517,8 +3533,7 @@ } concepts::NestedRequirement * - RebuildNestedRequirement( - concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + RebuildNestedRequirement(concepts::SubstitutionDiagnostic *SubstDiag) { return SemaRef.BuildNestedRequirement(SubstDiag); } @@ -12611,15 +12626,41 @@ E->getEndLoc()); } -template -ExprResult -TreeTransform::TransformConceptSpecializationExpr( - ConceptSpecializationExpr *E) { +namespace { +// TODO: refactor to use CreateSubstDiag from SemaTemplateInstantiate.cpp. +inline concepts::SubstitutionDiagnostic * +CreateSubstDiag(Sema &S, sema::TemplateDeductionInfo &Info) { + SmallString<128> Message; + SourceLocation ErrorLoc; + + PartialDiagnosticAt PDA(SourceLocation(), + PartialDiagnostic::NullDiagnostic{}); + Info.takeSFINAEDiagnostic(PDA); + PDA.second.EmitToString(S.getDiagnostics(), Message); + ErrorLoc = PDA.first; + char *MessageBuf = new (S.Context) char[Message.size()]; + std::copy(Message.begin(), Message.end(), MessageBuf); + return new (S.Context) concepts::SubstitutionDiagnostic{ + "todo fill me", ErrorLoc, StringRef(MessageBuf, Message.size())}; +} +} // namespace + +template +ExprResult TreeTransform::TransformConceptSpecializationExpr( + ConceptSpecializationExpr *E) { const ASTTemplateArgumentListInfo *Old = E->getTemplateArgsAsWritten(); TemplateArgumentListInfo TransArgs(Old->LAngleLoc, Old->RAngleLoc); - if (getDerived().TransformTemplateArguments(Old->getTemplateArgs(), - Old->NumTemplateArgs, TransArgs)) - return ExprError(); + { + Sema::SFINAETrap Trap(getSema()); + if (getDerived().TransformTemplateArguments( + Old->getTemplateArgs(), Old->NumTemplateArgs, TransArgs)) { + if (!E->isInstantiationDependent() || !Trap.hasErrorOccurred()) + return ExprError(); + // FIXME: Propagate detailed subtitution falilure diagnostics. + return getDerived().RebuildConceptSpecializationSubstitutionFailure( + E, CreateSubstDiag(SemaRef, **SemaRef.isSFINAEContext())); + } + } return getDerived().RebuildConceptSpecializationExpr( E->getNestedNameSpecifierLoc(), E->getTemplateKWLoc(), @@ -12720,7 +12761,7 @@ template concepts::ExprRequirement * TreeTransform::TransformExprRequirement(concepts::ExprRequirement *Req) { - llvm::PointerUnion TransExpr; + llvm::PointerUnion TransExpr; if (Req->isExprSubstitutionFailure()) TransExpr = Req->getExprSubstitutionDiagnostic(); else { @@ -12753,8 +12794,8 @@ Req->getNoexceptLoc(), std::move(*TransRetReq)); return getDerived().RebuildExprRequirement( - TransExpr.get(), - Req->isSimple(), Req->getNoexceptLoc(), std::move(*TransRetReq)); + TransExpr.get(), Req->isSimple(), + Req->getNoexceptLoc(), std::move(*TransRetReq)); } template diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -781,9 +781,9 @@ SourceLocation DiagLocation = Record.readSourceLocation(); std::string DiagMessage = Record.readString(); Satisfaction.Details.emplace_back( - ConstraintExpr, new (Record.getContext()) - ConstraintSatisfaction::SubstitutionDiagnostic{ - DiagLocation, DiagMessage}); + ConstraintExpr, + new (Record.getContext()) concepts::SubstitutionDiagnostic{ + "todo: fill substituted entity", DiagLocation, DiagMessage}); } else Satisfaction.Details.emplace_back(ConstraintExpr, Record.readExpr()); } @@ -806,14 +806,13 @@ readConstraintSatisfaction(Record)); } -static concepts::Requirement::SubstitutionDiagnostic * +static concepts::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}; + concepts::SubstitutionDiagnostic{SubstitutedEntity, DiagLoc, DiagMessage}; } void ASTStmtReader::VisitRequiresExpr(RequiresExpr *E) { @@ -850,8 +849,7 @@ auto Status = static_cast( Record.readInt()); - llvm::PointerUnion E; + llvm::PointerUnion E; if (Status == concepts::ExprRequirement::SS_ExprSubstitutionFailure) { E = readSubstitutionDiagnostic(Record); } else @@ -890,9 +888,9 @@ std::move(*Req), Status, SubstitutedConstraintExpr); else R = new (Record.getContext()) concepts::ExprRequirement( - E.get(), - RK == concepts::Requirement::RK_Simple, NoexceptLoc, - std::move(*Req)); + E.get(), + RK == concepts::Requirement::RK_Simple, NoexceptLoc, + std::move(*Req)); } break; case concepts::Requirement::RK_Nested: { if (/* IsSubstitutionDiagnostic */Record.readInt()) { diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -11,6 +11,7 @@ /// //===----------------------------------------------------------------------===// +#include "clang/AST/ASTConcept.h" #include "clang/AST/ExprOpenMP.h" #include "clang/Serialization/ASTRecordWriter.h" #include "clang/Sema/DeclSpec.h" @@ -412,19 +413,18 @@ if (E) Record.AddStmt(E); else { - auto *Diag = DetailRecord.second.get *>(); - Record.AddSourceLocation(Diag->first); - Record.AddString(Diag->second); + auto *Diag = + DetailRecord.second.get(); + Record.AddSourceLocation(Diag->DiagLoc); + Record.AddString(Diag->DiagMessage); } } } } static void -addSubstitutionDiagnostic( - ASTRecordWriter &Record, - const concepts::Requirement::SubstitutionDiagnostic *D) { +addSubstitutionDiagnostic(ASTRecordWriter &Record, + const concepts::SubstitutionDiagnostic *D) { Record.AddString(D->SubstitutedEntity); Record.AddSourceLocation(D->DiagLoc); Record.AddString(D->DiagMessage); @@ -467,8 +467,8 @@ Record.push_back(ExprReq->getKind()); Record.push_back(ExprReq->Status); if (ExprReq->isExprSubstitutionFailure()) { - addSubstitutionDiagnostic(Record, - ExprReq->Value.get()); + addSubstitutionDiagnostic( + Record, ExprReq->Value.get()); } else Record.AddStmt(ExprReq->Value.get()); if (ExprReq->getKind() == concepts::Requirement::RK_Compound) { diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -764,3 +764,53 @@ __iterator_traits_member_pointer_or_arrow_or_void> f; } }// namespace InheritedFromPartialSpec + +namespace nested_requirements { +template concept True = true; + +struct S { double value; }; + +template +concept Pipes = requires (T x) { + requires True || True; + requires True || True; +}; +template +concept Amps1 = requires (T x) { + requires True && True; + // expected-note@-1{{because 'todo fill me' would be invalid: member reference base type 'int' is not a structure or union}} +}; +template +concept Amps2 = requires (T x) { + requires True && True; +}; + +static_assert(Pipes); +static_assert(Pipes); + +static_assert(Amps1); +static_assert(!Amps1); + +static_assert(Amps2); +static_assert(!Amps2); + +template +void foo1() requires requires (T x) { // expected-note {{candidate template ignored: constraints not satisfied [with T = int]}} + requires + True // expected-note {{because 'todo fill me' would be invalid: member reference base type 'int' is not a structure or union}} + && True; +} {} +template void fooPipes() requires Pipes {} +template void fooAmps1() requires Amps1 {} +// expected-note@-1 {{candidate template ignored: constraints not satisfied [with T = int]}} \ +// expected-note@-1 {{because 'int' does not satisfy 'Amps1'}} + +void foo() { + foo1(); + foo1(); // expected-error {{no matching function for call to 'foo1'}} + fooPipes(); + fooPipes(); + fooAmps1(); + fooAmps1(); // expected-error {{no matching function for call to 'fooAmps1'}} +} +} \ No newline at end of file