Index: clang/include/clang/AST/ASTConcept.h =================================================================== --- clang/include/clang/AST/ASTConcept.h +++ clang/include/clang/AST/ASTConcept.h @@ -29,16 +29,24 @@ // The template-like entity that 'owns' the constraint checked here (can be a // constrained entity or a concept). const NamedDecl *ConstraintOwner = nullptr; + // Multi-dimensional template args, all flattened. llvm::SmallVector TemplateArgs; + void PopulateTemplateArgs(ArrayRef> TemplArgs) { + llvm::for_each(TemplArgs, [this](ArrayRef Args) { + TemplateArgs.append(Args.begin(), Args.end()); + }); + } + public: ConstraintSatisfaction() = default; ConstraintSatisfaction(const NamedDecl *ConstraintOwner, - ArrayRef TemplateArgs) : - ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs.begin(), - TemplateArgs.end()) { } + ArrayRef> TemplArgs) + : ConstraintOwner(ConstraintOwner) { + PopulateTemplateArgs(TemplArgs); + } using SubstitutionDiagnostic = std::pair; using Detail = llvm::PointerUnion; @@ -52,12 +60,14 @@ llvm::SmallVector, 4> Details; void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) { - Profile(ID, C, ConstraintOwner, TemplateArgs); + llvm::SmallVector> Args; + Args.push_back(TemplateArgs); + Profile(ID, C, ConstraintOwner, Args); } static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C, const NamedDecl *ConstraintOwner, - ArrayRef TemplateArgs); + ArrayRef> TemplateArgs); }; /// Pairs of unsatisfied atomic constraint expressions along with the Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -6911,6 +6911,8 @@ SourceLocation ConvLocation, CXXConversionDecl *Conv, Expr *Src); + // TODO: ERICH: Probably a better way to do this? + unsigned CheckingConstraintExpression = 0; /// Check whether the given expression is a valid constraint expression. /// A diagnostic is emitted if it is not, false is returned, and @@ -6984,9 +6986,16 @@ /// false otherwise. bool CheckConstraintSatisfaction( const NamedDecl *Template, ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, + /* ArrayRef TemplateArgs,*/ + MultiLevelTemplateArgumentList MLTAL, SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction); + bool CheckConstraintSatisfaction(const NamedDecl *Template, + ArrayRef ConstraintExprs, + ArrayRef TemplateArgs, + SourceRange TemplateIDRange, + ConstraintSatisfaction &Satisfaction); + /// \brief Check whether the given non-dependent constraint expression is /// satisfied. Returns false and updates Satisfaction with the satisfaction /// verdict if successful, emits a diagnostic and returns true if an error @@ -7020,9 +7029,10 @@ /// /// \returns true if the constrains are not satisfied or could not be checked /// for satisfaction, false if the constraints are satisfied. - bool EnsureTemplateArgumentListConstraints(TemplateDecl *Template, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange); + bool + EnsureTemplateArgumentListConstraints(TemplateDecl *Template, + MultiLevelTemplateArgumentList MLTAL, + SourceRange TemplateIDRange); /// \brief Emit diagnostics explaining why a constraint expression was deemed /// unsatisfied. @@ -8757,7 +8767,8 @@ MultiLevelTemplateArgumentList getTemplateInstantiationArgs( const NamedDecl *D, const TemplateArgumentList *Innermost = nullptr, - bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr); + bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr, + bool LookBeyondLambda = false, bool IncludeParentStructArgs = true); /// A context in which code is being synthesized (where a source location /// alone is not sufficient to identify the context). This covers template Index: clang/include/clang/Sema/Template.h =================================================================== --- clang/include/clang/Sema/Template.h +++ clang/include/clang/Sema/Template.h @@ -76,9 +76,12 @@ /// The template argument list at a certain template depth using ArgList = ArrayRef; + public: + // TODO: ERICH: should we make an accessor for this of some sort? /// The template argument lists, stored from the innermost template /// argument list (first) to the outermost template argument list (last). SmallVector TemplateArgumentLists; + private: /// The number of outer levels of template arguments that are not /// being substituted. Index: clang/lib/AST/ASTConcept.cpp =================================================================== --- clang/lib/AST/ASTConcept.cpp +++ clang/lib/AST/ASTConcept.cpp @@ -17,6 +17,7 @@ #include "clang/AST/TemplateBase.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" +#include using namespace clang; ASTConstraintSatisfaction::ASTConstraintSatisfaction(const ASTContext &C, @@ -59,9 +60,18 @@ void ConstraintSatisfaction::Profile( llvm::FoldingSetNodeID &ID, const ASTContext &C, - const NamedDecl *ConstraintOwner, ArrayRef TemplateArgs) { + const NamedDecl *ConstraintOwner, + llvm::ArrayRef> TemplateArgs) { ID.AddPointer(ConstraintOwner); - ID.AddInteger(TemplateArgs.size()); - for (auto &Arg : TemplateArgs) - Arg.Profile(ID, C); + // TODO ERICH: This depends on the 'depth substituting', which I think is + // insufficient here. Do we need to keep ALL the arguments, or does NamedDecl + // contain those? + ID.AddInteger( + std::accumulate(TemplateArgs.begin(), TemplateArgs.end(), 0, + [](int Acc, ArrayRef SubArgs) { + return Acc + SubArgs.size(); + })); + for (ArrayRef SubList : TemplateArgs) + for (auto &Arg : SubList) + Arg.Profile(ID, C); } Index: clang/lib/Parse/ParseTemplate.cpp =================================================================== --- clang/lib/Parse/ParseTemplate.cpp +++ clang/lib/Parse/ParseTemplate.cpp @@ -159,6 +159,11 @@ TemplateParams, RAngleLoc, OptionalRequiresClauseConstraintER.get())); } while (Tok.isOneOf(tok::kw_export, tok::kw_template)); + // TODO: ERICH: At one point this was valuable to move outside of the + // TemplateParameterDepthRAII scope above (by creating a new scope, and + // inverting this condition and the return after). However, I believe that + // caused other issues along the way, and was not particularly fruitful in the + // end. Marking this to remind you/perhaps be useful later. // Parse the actual template declaration. if (Tok.is(tok::kw_concept)) return ParseConceptDefinition( Index: clang/lib/Sema/SemaConcept.cpp =================================================================== --- clang/lib/Sema/SemaConcept.cpp +++ clang/lib/Sema/SemaConcept.cpp @@ -199,7 +199,7 @@ } static bool calculateConstraintSatisfaction( - Sema &S, const NamedDecl *Template, ArrayRef TemplateArgs, + Sema &S, const NamedDecl *Template, //ArrayRef TemplateArgs, SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { return calculateConstraintSatisfaction( @@ -219,8 +219,13 @@ return ExprError(); // We do not want error diagnostics escaping here. Sema::SFINAETrap Trap(S); + // TODO: ERICH: These are of course pretty hacky, hopefully we can + // come up iwth a way for Sema to just 'know' this during + // substitution. + ++S.CheckingConstraintExpression; SubstitutedExpression = S.SubstExpr(const_cast(AtomicExpr), MLTAL); + --S.CheckingConstraintExpression; // Substitution might have stripped off a contextual conversion to // bool if this is the operand of an '&&' or '||'. For example, we // might lose an lvalue-to-rvalue conversion here. If so, put it back @@ -270,7 +275,8 @@ static bool CheckConstraintSatisfaction(Sema &S, const NamedDecl *Template, ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, + // ArrayRef TemplateArgs, + MultiLevelTemplateArgumentList MLTAL, SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { if (ConstraintExprs.empty()) { @@ -278,24 +284,33 @@ return false; } - for (auto& Arg : TemplateArgs) - if (Arg.isInstantiationDependent()) { - // No need to check satisfaction for dependent constraint expressions. - Satisfaction.IsSatisfied = true; - return false; - } + for (auto &TemplateArgs: MLTAL.TemplateArgumentLists) + for (auto& Arg : TemplateArgs) + if (Arg.isInstantiationDependent()) { + // No need to check satisfaction for dependent constraint expressions. + Satisfaction.IsSatisfied = true; + return false; + } - Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(), + // TODO: ERICH: I have no idea what this does, but it seems to work ok with + // just the base-level? Do we need to create new ones of these to handle the + // multi-level version? + Sema::InstantiatingTemplate Inst( + S, TemplateIDRange.getBegin(), Sema::InstantiatingTemplate::ConstraintsCheck{}, - const_cast(Template), TemplateArgs, TemplateIDRange); + const_cast(Template), + (MLTAL.TemplateArgumentLists.size() + ? MLTAL.TemplateArgumentLists[MLTAL.TemplateArgumentLists.size() - 1] + : ArrayRef{}), + TemplateIDRange); if (Inst.isInvalid()) return true; - MultiLevelTemplateArgumentList MLTAL; - MLTAL.addOuterTemplateArguments(TemplateArgs); + //MultiLevelTemplateArgumentList MLTAL; + //MLTAL.addOuterTemplateArguments(TemplateArgs); for (const Expr *ConstraintExpr : ConstraintExprs) { - if (calculateConstraintSatisfaction(S, Template, TemplateArgs, + if (calculateConstraintSatisfaction(S, Template,// TemplateArgs, TemplateIDRange.getBegin(), MLTAL, ConstraintExpr, Satisfaction)) return true; @@ -311,7 +326,20 @@ bool Sema::CheckConstraintSatisfaction( const NamedDecl *Template, ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, SourceRange TemplateIDRange, + ArrayRef TemplateArgs, + SourceRange TemplateIDRange, + ConstraintSatisfaction &OutSatisfaction) { + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(TemplateArgs); + return CheckConstraintSatisfaction(Template, ConstraintExprs, MLTAL, + TemplateIDRange, OutSatisfaction); +} + +bool Sema::CheckConstraintSatisfaction( + const NamedDecl *Template, ArrayRef ConstraintExprs, + /*ArrayRef TemplateArgs,*/ + MultiLevelTemplateArgumentList MLTAL, + SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) { if (ConstraintExprs.empty()) { OutSatisfaction.IsSatisfied = true; @@ -323,18 +351,18 @@ ConstraintSatisfaction *Satisfaction = nullptr; bool ShouldCache = LangOpts.ConceptSatisfactionCaching && Template; if (ShouldCache) { - ConstraintSatisfaction::Profile(ID, Context, Template, TemplateArgs); + ConstraintSatisfaction::Profile(ID, Context, Template, MLTAL.TemplateArgumentLists); Satisfaction = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos); if (Satisfaction) { OutSatisfaction = *Satisfaction; return false; } - Satisfaction = new ConstraintSatisfaction(Template, TemplateArgs); + Satisfaction = new ConstraintSatisfaction(Template, MLTAL.TemplateArgumentLists); } else { Satisfaction = &OutSatisfaction; } if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, - TemplateArgs, TemplateIDRange, + MLTAL, TemplateIDRange, *Satisfaction)) { if (ShouldCache) delete Satisfaction; @@ -362,11 +390,25 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, ConstraintSatisfaction &Satisfaction, SourceLocation UsageLoc) { - const Expr *RC = FD->getTrailingRequiresClause(); - if (RC->isInstantiationDependent()) { + // TODO: ERICH:: the constraint is possibly still dependent here as it likely + // hasn't been instantiated yet, so check based on whether the FUNCTION is + // dependent. + //if (RC->isInstantiationDependent()) { + // Satisfaction.IsSatisfied = true; + // return false; + //} + if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate || + // TODO: ERICH: FunctionTemplateSpecialization doesn't seem to fit here, + // since it'll be non-dependent, but I think these will all be checked + // 'the other way' + FD->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization || + FD->getTemplatedKind() == + FunctionDecl::TK_DependentFunctionTemplateSpecialization) { Satisfaction.IsSatisfied = true; return false; } + const Expr *RC = FD->getTrailingRequiresClause(); Qualifiers ThisQuals; CXXRecordDecl *Record = nullptr; if (auto *Method = dyn_cast(FD)) { @@ -374,30 +416,56 @@ Record = const_cast(Method->getParent()); } CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); + LocalInstantiationScope Scope(*this); + + MultiLevelTemplateArgumentList MLTAL = + getTemplateInstantiationArgs(FD, nullptr, /*RelativeToPrimary*/ true, + /*Pattern*/ nullptr, + /*LookBeyondLambda*/ true); + if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) { + InstantiatingTemplate Inst(*this, FD->getPointOfInstantiation(), + InstantiatingTemplate::ConstraintsCheck{}, FD->getPrimaryTemplate(), + {}, SourceRange()); + if (Inst.isInvalid()) + return true; + + // TODO: ERICH: Can we do something about this const-cast here? + if (addInstantiatedParametersToScope( + const_cast(FD), + // TODO: ERICH: in SemaTemplateInstantiateDecl, we had to do this on + // MLTAL2 (with additional outer args), do we need to do so here? Do + // we have to do the 2nd addInstantiatedParametersToScope? + FD->getPrimaryTemplate()->getTemplatedDecl(), Scope, MLTAL)) + return true; + } + // We substitute with empty arguments in order to rebuild the atomic // constraint in a constant-evaluated context. // FIXME: Should this be a dedicated TreeTransform? return CheckConstraintSatisfaction( - FD, {RC}, /*TemplateArgs=*/{}, + FD, {RC}, MLTAL, SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()), Satisfaction); } bool Sema::EnsureTemplateArgumentListConstraints( - TemplateDecl *TD, ArrayRef TemplateArgs, + TemplateDecl *TD, MultiLevelTemplateArgumentList MLTAL, SourceRange TemplateIDRange) { ConstraintSatisfaction Satisfaction; llvm::SmallVector AssociatedConstraints; TD->getAssociatedConstraints(AssociatedConstraints); - if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgs, + if (CheckConstraintSatisfaction(TD, AssociatedConstraints, MLTAL, TemplateIDRange, Satisfaction)) return true; if (!Satisfaction.IsSatisfied) { SmallString<128> TemplateArgString; TemplateArgString = " "; + // TODO: ERICH: Is this acceptable? Should we be printing more than just the + // bottom level? TemplateArgString += getTemplateArgumentBindingsText( - TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size()); + TD->getTemplateParameters(), MLTAL.getInnermost().data(), + MLTAL.getInnermost().size()); Diag(TemplateIDRange.getBegin(), diag::err_template_arg_list_constraints_not_satisfied) Index: clang/lib/Sema/SemaExprCXX.cpp =================================================================== --- clang/lib/Sema/SemaExprCXX.cpp +++ clang/lib/Sema/SemaExprCXX.cpp @@ -8878,7 +8878,8 @@ Sema::BuildNestedRequirement(Expr *Constraint) { ConstraintSatisfaction Satisfaction; if (!Constraint->isInstantiationDependent() && - CheckConstraintSatisfaction(nullptr, {Constraint}, /*TemplateArgs=*/{}, + CheckConstraintSatisfaction(nullptr, {Constraint}, + /*TemplateArgs=*/ArrayRef{}, Constraint->getSourceRange(), Satisfaction)) return nullptr; return new (Context) concepts::NestedRequirement(Context, Constraint, Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -3670,6 +3670,8 @@ // Check that the template argument list is well-formed for this // template. SmallVector Converted; + // TODO: ERICH: This is the e ntry point, but I think the info below sets up + // the template arguments. if (CheckTemplateArgumentList(Template, TemplateLoc, TemplateArgs, false, Converted, /*UpdateArgsWithConversions=*/true)) @@ -3693,6 +3695,7 @@ TemplateArgLists.addOuterTemplateArguments(&StackTemplateArgs); TemplateArgLists.addOuterRetainedLevels( AliasTemplate->getTemplateParameters()->getDepth()); + // TODO: Can we use the bit above? LocalInstantiationScope Scope(*this); InstantiatingTemplate Inst(*this, TemplateLoc, Template); @@ -5903,13 +5906,27 @@ if (UpdateArgsWithConversions) TemplateArgs = std::move(NewArgs); - if (!PartialTemplateArgs && - EnsureTemplateArgumentListConstraints( - Template, Converted, SourceRange(TemplateLoc, - TemplateArgs.getRAngleLoc()))) { - if (ConstraintsNotSatisfied) - *ConstraintsNotSatisfied = true; - return true; + if (!PartialTemplateArgs) { + TemplateArgumentList StackTemplateArgs(TemplateArgumentList::OnStack, + Converted); + // TODO: ERICH: Calling this function caused an assert due to trying to get + // the template instantiation args when the 'parent' type isn't instantiated. + // We should be skipipng the constraints check if any of the 'parent' types + // are not instantiated, but we currently just remove the assert and count + // on it getting the dependent args (which are checked in the constraints + // check). + MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs( + Template, &StackTemplateArgs, /*RelativeToPrimary*/ true, + /*Pattern*/ nullptr, + /*LookBeyondLambda*/ true, /*PIckupParentStructs*/ true); + + if (EnsureTemplateArgumentListConstraints( + Template, MLTAL, + SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) { + if (ConstraintsNotSatisfied) + *ConstraintsNotSatisfied = true; + return true; + } } return false; Index: clang/lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- clang/lib/Sema/SemaTemplateDeduction.cpp +++ clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4572,6 +4572,7 @@ if (S.CheckTemplateArgumentList(Concept, SourceLocation(), TemplateArgs, /*PartialTemplateArgs=*/false, Converted)) return Sema::DAR_FailedAlreadyDiagnosed; + if (S.CheckConstraintSatisfaction(Concept, {Concept->getConstraintExpr()}, Converted, TypeLoc.getLocalSourceRange(), Satisfaction)) Index: clang/lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiate.cpp +++ clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -57,7 +57,8 @@ /// friend function template specializations. MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( const NamedDecl *D, const TemplateArgumentList *Innermost, - bool RelativeToPrimary, const FunctionDecl *Pattern) { + bool RelativeToPrimary, const FunctionDecl *Pattern, bool LookBeyondLambda, + bool IncludeParentStructArgs) { // Accumulate the set of template argument lists in this structure. MultiLevelTemplateArgumentList Result; @@ -153,11 +154,13 @@ break; // If this function is a generic lambda specialization, we are done. - if (isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) + if (!LookBeyondLambda && + isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) break; } else if (Function->getDescribedFunctionTemplate()) { - assert(Result.getNumSubstitutedLevels() == 0 && + assert((IncludeParentStructArgs || + Result.getNumSubstitutedLevels() == 0) && "Outer template not instantiated?"); } @@ -174,10 +177,23 @@ } } else if (const auto *Rec = dyn_cast(Ctx)) { if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) { - assert(Result.getNumSubstitutedLevels() == 0 && + // TODO: ERICH: This doesn't seem to make sense? Part of how this can + // be used is to get the template arguments of a sub-class, so I don't + // know why a fully instantiated outer template would be necessary. + assert((IncludeParentStructArgs || + Result.getNumSubstitutedLevels() == 0) && "Outer template not instantiated?"); if (ClassTemplate->isMemberSpecialization()) break; + if (IncludeParentStructArgs) { + // This SHOULD be an 'injected class name type' here. + QualType RecordType = Context.getTypeDeclType(Rec); + QualType Injected = cast(RecordType) + ->getInjectedSpecializationType(); + const TemplateSpecializationType *InjectedType = + cast(Injected); + Result.addOuterTemplateArguments(InjectedType->template_arguments()); + } } } @@ -2307,15 +2323,22 @@ const MultiLevelTemplateArgumentList &TemplateArgs) { const ASTTemplateArgumentListInfo *TemplArgInfo = TC->getTemplateArgsAsWritten(); + TemplateArgumentListInfo InstArgs; if (TemplArgInfo) { InstArgs.setLAngleLoc(TemplArgInfo->LAngleLoc); InstArgs.setRAngleLoc(TemplArgInfo->RAngleLoc); + + // TODO: ERICH: Still do this if we skip it before? on SemaTemplateInstantiateDecl.cpp:2724? + // // ERICH: We don't want to substitute these until checked. +// for(const TemplateArgumentLoc &TAL : TemplArgInfo->arguments()) + // InstArgs.addArgument(TAL); if (SubstTemplateArguments(TemplArgInfo->arguments(), TemplateArgs, InstArgs)) return true; } + return AttachTypeConstraint( TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(), TC->getNamedConcept(), &InstArgs, Inst, @@ -2389,8 +2412,18 @@ if (Inst && !Inst->getTypeConstraint()) { // TODO: Concepts: do not instantiate the constraint (delayed constraint // substitution) - if (SubstTypeConstraint(Inst, TC, TemplateArgs)) - return nullptr; + // TODO: ERICH: Is this condition right? This SEEMS like the right thing + // to do? + if (CheckingConstraintExpression) { + if (SubstTypeConstraint(Inst, TC, TemplateArgs)) + return nullptr; + } else { + Inst->setTypeConstraint(TC->getNestedNameSpecifierLoc(), + TC->getConceptNameInfo(), + TC->getNamedConcept(), TC->getNamedConcept(), + TC->getTemplateArgsAsWritten(), + TC->getImmediatelyDeclaredConstraint()); + } } } } Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2034,6 +2034,7 @@ // FIXME: Concepts: Do not substitute into constraint expressions Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); + /* if (TrailingRequiresClause) { EnterExpressionEvaluationContext ConstantEvaluated( SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); @@ -2045,6 +2046,7 @@ if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause)) return nullptr; } + */ // If we're instantiating a local function declaration, put the result // in the enclosing namespace; otherwise we need to find the instantiated @@ -2391,6 +2393,7 @@ // FIXME: Concepts: Do not substitute into constraint expressions Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); +/* if (TrailingRequiresClause) { EnterExpressionEvaluationContext ConstantEvaluated( SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); @@ -2405,6 +2408,7 @@ if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause)) return nullptr; } + */ DeclContext *DC = Owner; if (isFriend) { @@ -2736,11 +2740,23 @@ // Invented template parameter type constraints will be instantiated with // the corresponding auto-typed parameter as it might reference other // parameters. - - // TODO: Concepts: do not instantiate the constraint (delayed constraint - // substitution) - if (SemaRef.SubstTypeConstraint(Inst, TC, TemplateArgs)) - return nullptr; + const ASTTemplateArgumentListInfo *ArgsAsWritten = + TC->getTemplateArgsAsWritten(); + + // TODO: ERICH: Can we come up with a somewhat less hacky way of doing + // this? Would be nice if this was some sort of state of the + // TemplatedeclInstantiator. + if (SemaRef.CheckingConstraintExpression > 0) { + if (SemaRef.SubstTypeConstraint(Inst, TC, TemplateArgs)) + return nullptr; + } else { + // TODO: ERICH: probably want to find a way to duplicate the args + // rather than reusing them? + Inst->setTypeConstraint(TC->getNestedNameSpecifierLoc(), + TC->getConceptNameInfo(), TC->getNamedConcept(), + TC->getNamedConcept(), ArgsAsWritten, + TC->getImmediatelyDeclaredConstraint()); + } } } if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) { @@ -3983,6 +3999,7 @@ // FIXME: Concepts: Substitution into requires clause should only happen when // checking satisfaction. + /* Expr *InstRequiresClause = nullptr; if (Expr *E = L->getRequiresClause()) { EnterExpressionEvaluationContext ConstantEvaluated( @@ -3992,7 +4009,9 @@ return nullptr; } InstRequiresClause = Res.get(); - } + }*/ + + Expr *InstRequiresClause = L->getRequiresClause(); TemplateParameterList *InstL = TemplateParameterList::Create(SemaRef.Context, L->getTemplateLoc(), @@ -4588,6 +4607,7 @@ // PushDeclContext because we don't have a scope. Sema::ContextRAII savedContext(*this, Decl); LocalInstantiationScope Scope(*this); + MultiLevelTemplateArgumentList MLTAL; // If this is not an explicit specialization - we need to get the instantiated // version of the template arguments and add them to scope for the @@ -4598,12 +4618,33 @@ TemplateArgs, SourceRange()); if (Inst.isInvalid()) return true; - MultiLevelTemplateArgumentList MLTAL( - *Decl->getTemplateSpecializationArgs()); + + // TODO: ERICH: Wonder if we could move this to the CheckConstraintSatisfaacton wrapper? + // TODO: ERICH: Should pattern be the PrimaryTemplate below? + // TODO: ERICH: Why do we have to call addInstantiatedParametersToScope 2x? + MLTAL = getTemplateInstantiationArgs( + Decl, nullptr, /*RelativeToPrimary*/ true, /*Pattern*/ nullptr, + /*LookBeyondLambda*/ true); + MultiLevelTemplateArgumentList MLTAL2 = getTemplateInstantiationArgs( + Decl, nullptr, /*RelativeToPrimary*/ true, /*Pattern*/ nullptr, + /*LookBeyondLambda*/ true); + MLTAL2.addOuterTemplateArguments(TemplateArgs); + if (addInstantiatedParametersToScope( - Decl, Decl->getPrimaryTemplate()->getTemplatedDecl(), Scope, MLTAL)) + Decl, Decl->getPrimaryTemplate()->getTemplatedDecl(), Scope, + MLTAL2)) return true; + const FunctionTemplateDecl *PrimaryTemplate = Decl->getPrimaryTemplate(); + if (const auto *FromMemTempl = + PrimaryTemplate->getInstantiatedFromMemberTemplate()) + if (addInstantiatedParametersToScope( + Decl, FromMemTempl->getTemplatedDecl(), Scope, MLTAL)) + return true; + + } else { + MLTAL.addOuterTemplateArguments(TemplateArgs); } + Qualifiers ThisQuals; CXXRecordDecl *Record = nullptr; if (auto *Method = dyn_cast(Decl)) { @@ -4611,7 +4652,7 @@ Record = Method->getParent(); } CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); - return CheckConstraintSatisfaction(Template, TemplateAC, TemplateArgs, + return CheckConstraintSatisfaction(Template, TemplateAC, MLTAL, PointOfInstantiation, Satisfaction); } Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -12920,11 +12920,13 @@ } // Transform the trailing requires clause - ExprResult NewTrailingRequiresClause; + ExprResult NewTrailingRequiresClause = E->getCallOperator()->getTrailingRequiresClause();; + /* if (Expr *TRC = E->getCallOperator()->getTrailingRequiresClause()) // FIXME: Concepts: Substitution into requires clause should only happen // when checking satisfaction. NewTrailingRequiresClause = getDerived().TransformExpr(TRC); + */ // Create the local class that will describe the lambda. // FIXME: KnownDependent below is wrong when substituting inside a templated Index: clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp =================================================================== --- clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp +++ clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp @@ -90,3 +90,22 @@ static_assert(C{}); // expected-note{{while checking constraint satisfaction for template 'C' required here}} static_assert(D{}); // expected-note{{while checking constraint satisfaction for template 'D' required here}} + + +namespace DelayedInst { +template +struct AAA { + template requires (sizeof(T) == I) // expected-note {{because 'sizeof(int) == 5U' (4 == 5) evaluated to false}} + struct B { + static constexpr int a = 0; + }; + + static constexpr auto foo() { + return B::a; // expected-error{{constraints not satisfied for class template 'B' [with T = int]}} + } +}; + +constexpr auto x = AAA<4>::foo(); +constexpr auto y = AAA<5>::foo(); // expected-note {{in instantiation of member function 'DelayedInst::AAA<5>::foo' requested here}} + +} Index: clang/test/SemaTemplate/instantiate-requires-clause.cpp =================================================================== --- clang/test/SemaTemplate/instantiate-requires-clause.cpp +++ clang/test/SemaTemplate/instantiate-requires-clause.cpp @@ -33,13 +33,25 @@ template struct S { template - static constexpr auto f(U const index) requires(index, true) { + static constexpr auto f(U const index) requires(index, true) + { return true; } }; static_assert(S::f(1)); +template +struct S1 { + // expected-note@+3 {{candidate template ignored: constraints not satisfied [with U = int]}} + // expected-note@+2 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + template + static constexpr auto f(U const index) requires(U::foo) { return true; } +}; + +// expected-error@+1 {{no matching function for call to 'f'}} +static_assert(S1::f(1)); + constexpr auto value = 0; template Index: clang/test/SemaTemplate/trailing-return-short-circuit.cpp =================================================================== --- /dev/null +++ clang/test/SemaTemplate/trailing-return-short-circuit.cpp @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s + +template +requires(sizeof(T) > 2) || T::value // #FOO_REQ +void Foo(T){}; // #FOO + +template +void TrailingReturn(T) // #TRAILING +requires(sizeof(T) > 2) || T::value // #TRAILING_REQ +{}; +template +struct HasValue { + static constexpr bool value = B; +}; +static_assert(sizeof(HasValue) <= 2); + +template +struct HasValueLarge { + static constexpr bool value = B; + int I; +}; +static_assert(sizeof(HasValueLarge) > 2); + +void usage() { + // Passes the 1st check, short-circuit so the 2nd ::value is not evaluated. + Foo(1.0); + TrailingReturn(1.0); + + // Fails the 1st check, but has a ::value, so the check happens correctly. + Foo(HasValue{}); + TrailingReturn(HasValue{}); + + // Passes the 1st check, but would have passed the 2nd one. + Foo(HasValueLarge{}); + TrailingReturn(HasValueLarge{}); + + // Fails the 1st check, fails 2nd because there is no ::value. + Foo(true); + // expected-error@-1{{no matching function for call to 'Foo'}} + // expected-note@#FOO{{candidate template ignored: constraints not satisfied [with T = bool]}} + // expected-note@#FOO_REQ{{because 'sizeof(_Bool) > 2' (1 > 2) evaluated to false}} + // expected-note@#FOO_REQ{{because substituted constraint expression is ill-formed: type 'bool' cannot be used prior to '::' because it has no members}} + + TrailingReturn(true); + // expected-error@-1{{no matching function for call to 'TrailingReturn'}} + // expected-note@#TRAILING{{candidate template ignored: constraints not satisfied [with T = bool]}} + // expected-note@#TRAILING_REQ{{because 'sizeof(_Bool) > 2' (1 > 2) evaluated to false}} + // expected-note@#TRAILING_REQ{{because substituted constraint expression is ill-formed: type 'bool' cannot be used prior to '::' because it has no members}} + + // Fails the 1st check, fails 2nd because ::value is false. + Foo(HasValue{}); + // expected-error@-1 {{no matching function for call to 'Foo'}} + // expected-note@#FOO{{candidate template ignored: constraints not satisfied [with T = HasValue]}} + // expected-note@#FOO_REQ{{because 'sizeof(HasValue) > 2' (1 > 2) evaluated to false}} + // expected-note@#FOO_REQ{{and 'HasValue::value' evaluated to false}} + TrailingReturn(HasValue{}); + // expected-error@-1 {{no matching function for call to 'TrailingReturn'}} + // expected-note@#TRAILING{{candidate template ignored: constraints not satisfied [with T = HasValue]}} + // expected-note@#TRAILING_REQ{{because 'sizeof(HasValue) > 2' (1 > 2) evaluated to false}} + // expected-note@#TRAILING_REQ{{and 'HasValue::value' evaluated to false}} +}