Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -6943,7 +6943,26 @@ LocalInstantiationScope &Scope, const MultiLevelTemplateArgumentList &TemplateArgs); + llvm::Optional + SetupConstraintCheckingTemplateArgumentsAndScope( + FunctionDecl *FD, llvm::Optional> TemplateArgs, + LocalInstantiationScope &Scope); + + // Keep track of whether we are evaluating a constraint. + unsigned ConstraintEvaluationDepth = 0; + + + class ConstraintEvalRAII { + Sema &S; + public: + ConstraintEvalRAII(Sema &S) : S(S) { ++S.ConstraintEvaluationDepth; } + ~ConstraintEvalRAII() { --S.ConstraintEvaluationDepth; } + }; + public: + bool IsEvaluatingAConstraint() { + return ConstraintEvaluationDepth > 0; + } const NormalizedConstraint * getNormalizedAssociatedConstraints( NamedDecl *ConstrainedDecl, ArrayRef AssociatedConstraints); @@ -6974,7 +6993,8 @@ /// \param ConstraintExprs a list of constraint expressions, treated as if /// they were 'AND'ed together. /// \param TemplateArgs the list of template arguments to substitute into the - /// constraint expression. + /// constraint expression, which is multi-level since we have to substitute + /// ALL levels for the purposes of checking. /// \param TemplateIDRange The source range of the template id that /// caused the constraints check. /// \param Satisfaction if true is returned, will contain details of the @@ -6984,7 +7004,7 @@ /// false otherwise. bool CheckConstraintSatisfaction( const NamedDecl *Template, ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, + const MultiLevelTemplateArgumentList &TemplateArgList, SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction); /// \brief Check whether the given non-dependent constraint expression is @@ -7020,9 +7040,9 @@ /// /// \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 TemplateArgs, + SourceRange TemplateIDRange); /// \brief Emit diagnostics explaining why a constraint expression was deemed /// unsatisfied. @@ -8757,7 +8777,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 IncludeContainingStruct = false); /// A context in which code is being synthesized (where a source location /// alone is not sufficient to identify the context). This covers template @@ -9478,6 +9499,11 @@ ExtParameterInfoBuilder &ParamInfos); ExprResult SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs); + // Unlike the above, this evaluates constraints, which should only happen at + // 'constraint checking' time. + ExprResult + SubstConstraintExpr(Expr *E, + const MultiLevelTemplateArgumentList &TemplateArgs); /// Substitute the given template arguments into a list of /// expressions, expanding pack expansions if required. Index: clang/include/clang/Sema/Template.h =================================================================== --- clang/include/clang/Sema/Template.h +++ clang/include/clang/Sema/Template.h @@ -158,6 +158,14 @@ return !(*this)(Depth, Index).isNull(); } + bool isAnyArgInstantiationDependent() const { + for (ArgList List : TemplateArgumentLists) + for (const TemplateArgument &TA : List) + if (TA.isInstantiationDependent()) + return true; + return false; + } + /// Clear out a specific template argument. void setArgument(unsigned Depth, unsigned Index, TemplateArgument Arg) { @@ -197,6 +205,11 @@ const ArgList &getInnermost() const { return TemplateArgumentLists.front(); } + + /// Retrieve the outermost template argument list. + const ArgList &getOutermost() const { + return TemplateArgumentLists.back(); + } }; /// The context in which partial ordering of function templates occurs. Index: clang/lib/Sema/SemaConcept.cpp =================================================================== --- clang/lib/Sema/SemaConcept.cpp +++ clang/lib/Sema/SemaConcept.cpp @@ -199,9 +199,9 @@ } static bool calculateConstraintSatisfaction( - Sema &S, const NamedDecl *Template, ArrayRef TemplateArgs, - SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL, - const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { + Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc, + const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr, + ConstraintSatisfaction &Satisfaction) { return calculateConstraintSatisfaction( S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) { EnterExpressionEvaluationContext ConstantEvaluated( @@ -219,8 +219,8 @@ return ExprError(); // We do not want error diagnostics escaping here. Sema::SFINAETrap Trap(S); - SubstitutedExpression = S.SubstExpr(const_cast(AtomicExpr), - MLTAL); + SubstitutedExpression = + S.SubstConstraintExpr(const_cast(AtomicExpr), MLTAL); // 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 @@ -268,36 +268,40 @@ }); } -static bool CheckConstraintSatisfaction(Sema &S, const NamedDecl *Template, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction) { +static bool CheckConstraintSatisfaction( + Sema &S, const NamedDecl *Template, ArrayRef ConstraintExprs, + const MultiLevelTemplateArgumentList &TemplateArgsList, + SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { if (ConstraintExprs.empty()) { Satisfaction.IsSatisfied = true; return false; } - for (auto& Arg : TemplateArgs) - if (Arg.isInstantiationDependent()) { - // No need to check satisfaction for dependent constraint expressions. - Satisfaction.IsSatisfied = true; - return false; - } + if (TemplateArgsList.isAnyArgInstantiationDependent()) { + // 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(), + ArrayRef TemplateArgs = + TemplateArgsList.getNumSubstitutedLevels() > 0 + ? TemplateArgsList.getOutermost() + : ArrayRef{}; + + Sema::InstantiatingTemplate Inst( + S, TemplateIDRange.getBegin(), Sema::InstantiatingTemplate::ConstraintsCheck{}, - const_cast(Template), TemplateArgs, TemplateIDRange); + const_cast(Template), TemplateArgs, + TemplateIDRange); if (Inst.isInvalid()) return true; - MultiLevelTemplateArgumentList MLTAL; - MLTAL.addOuterTemplateArguments(TemplateArgs); - for (const Expr *ConstraintExpr : ConstraintExprs) { - if (calculateConstraintSatisfaction(S, Template, TemplateArgs, - TemplateIDRange.getBegin(), MLTAL, - ConstraintExpr, Satisfaction)) + if (calculateConstraintSatisfaction(S, Template, TemplateIDRange.getBegin(), + TemplateArgsList, ConstraintExpr, + Satisfaction)) return true; if (!Satisfaction.IsSatisfied) // [temp.constr.op] p2 @@ -311,13 +315,18 @@ bool Sema::CheckConstraintSatisfaction( const NamedDecl *Template, ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, SourceRange TemplateIDRange, - ConstraintSatisfaction &OutSatisfaction) { + const MultiLevelTemplateArgumentList &TemplateArgsList, + SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) { if (ConstraintExprs.empty()) { OutSatisfaction.IsSatisfied = true; return false; } + ArrayRef TemplateArgs = + TemplateArgsList.getNumSubstitutedLevels() > 0 + ? TemplateArgsList.getOutermost() + : ArrayRef{}; + llvm::FoldingSetNodeID ID; void *InsertPos; ConstraintSatisfaction *Satisfaction = nullptr; @@ -334,7 +343,7 @@ Satisfaction = &OutSatisfaction; } if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, - TemplateArgs, TemplateIDRange, + TemplateArgsList, TemplateIDRange, *Satisfaction)) { if (ShouldCache) delete Satisfaction; @@ -359,14 +368,65 @@ }); } +llvm::Optional +Sema::SetupConstraintCheckingTemplateArgumentsAndScope( + FunctionDecl *FD, llvm::Optional> TemplateArgs, + LocalInstantiationScope &Scope) { + MultiLevelTemplateArgumentList MLTAL; + + if (FD->isTemplateInstantiation()) { + MLTAL = getTemplateInstantiationArgs( + FD, nullptr, /*RelativeToPrimary*/ true, /*Pattern*/ nullptr, + /*LookBeyondLambda*/ true); + MultiLevelTemplateArgumentList MLTALWithTemplateArgs = MLTAL; + if (TemplateArgs) + MLTALWithTemplateArgs.addOuterTemplateArguments(*TemplateArgs); + + if (const FunctionTemplateDecl *PrimaryTemplate = + FD->getPrimaryTemplate()) { + InstantiatingTemplate Inst( + *this, FD->getPointOfInstantiation(), + InstantiatingTemplate::ConstraintsCheck{}, FD->getPrimaryTemplate(), + TemplateArgs ? *TemplateArgs : ArrayRef{}, + SourceRange()); + if (Inst.isInvalid()) + return {}; + if (addInstantiatedParametersToScope(FD, + PrimaryTemplate->getTemplatedDecl(), + Scope, MLTALWithTemplateArgs)) + return {}; + + if (const auto *FromMemTmpl = + PrimaryTemplate->getInstantiatedFromMemberTemplate()) + if (addInstantiatedParametersToScope( + FD, FromMemTmpl->getTemplatedDecl(), Scope, MLTAL)) + return {}; + } + } else if (TemplateArgs) { + MLTAL.addOuterTemplateArguments(*TemplateArgs); + } + return MLTAL; +} + bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, ConstraintSatisfaction &Satisfaction, SourceLocation UsageLoc) { - const Expr *RC = FD->getTrailingRequiresClause(); - if (RC->isInstantiationDependent()) { + // Don't check constraints if the function is dependent. Also don't check if + // this is a function template specialization, as the call to + // CheckinstantiatedFunctionTemplateConstraints after this will check it + // better. + if (FD->isDependentContext() || + FD->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) { Satisfaction.IsSatisfied = true; return false; } + + LocalInstantiationScope Scope(*this); + llvm::Optional MLTAL = + SetupConstraintCheckingTemplateArgumentsAndScope( + const_cast(FD), {}, Scope); + Qualifiers ThisQuals; CXXRecordDecl *Record = nullptr; if (auto *Method = dyn_cast(FD)) { @@ -377,14 +437,15 @@ // We substitute with empty arguments in order to rebuild the atomic // constraint in a constant-evaluated context. // FIXME: Should this be a dedicated TreeTransform? + const Expr *RC = FD->getTrailingRequiresClause(); 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 TemplateArgs, SourceRange TemplateIDRange) { ConstraintSatisfaction Satisfaction; llvm::SmallVector AssociatedConstraints; @@ -397,7 +458,8 @@ SmallString<128> TemplateArgString; TemplateArgString = " "; TemplateArgString += getTemplateArgumentBindingsText( - TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size()); + TD->getTemplateParameters(), TemplateArgs.getInnermost().data(), + TemplateArgs.getInnermost().size()); Diag(TemplateIDRange.getBegin(), diag::err_template_arg_list_constraints_not_satisfied) @@ -429,21 +491,13 @@ Sema::ContextRAII savedContext(*this, Decl); LocalInstantiationScope Scope(*this); - // 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 - // substitution. - if (Decl->isTemplateInstantiation()) { - InstantiatingTemplate Inst(*this, Decl->getPointOfInstantiation(), - InstantiatingTemplate::ConstraintsCheck{}, Decl->getPrimaryTemplate(), - TemplateArgs, SourceRange()); - if (Inst.isInvalid()) - return true; - MultiLevelTemplateArgumentList MLTAL( - *Decl->getTemplateSpecializationArgs()); - if (addInstantiatedParametersToScope( - Decl, Decl->getPrimaryTemplate()->getTemplatedDecl(), Scope, MLTAL)) - return true; - } + Optional MLTAL = + SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs, + Scope); + + if (!MLTAL) + return true; + Qualifiers ThisQuals; CXXRecordDecl *Record = nullptr; if (auto *Method = dyn_cast(Decl)) { @@ -451,7 +505,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/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -4684,9 +4684,11 @@ bool AreArgsDependent = TemplateSpecializationType::anyDependentTemplateArguments(*TemplateArgs, Converted); + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(Converted); if (!AreArgsDependent && CheckConstraintSatisfaction( - NamedConcept, {NamedConcept->getConstraintExpr()}, Converted, + NamedConcept, {NamedConcept->getConstraintExpr()}, MLTAL, SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(), TemplateArgs->getRAngleLoc()), Satisfaction)) @@ -5903,13 +5905,20 @@ 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); + MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs( + Template, &StackTemplateArgs, /*RelativeToPrimary*/ true, + /*Pattern*/ nullptr, + /*LookBeyondLambda*/ true, /*IncludeContainingStruct*/ 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 @@ -2791,8 +2791,10 @@ TemplateDeductionInfo& Info) { llvm::SmallVector AssociatedConstraints; Template->getAssociatedConstraints(AssociatedConstraints); + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(DeducedArgs); if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, - DeducedArgs, Info.getLocation(), + MLTAL, Info.getLocation(), Info.AssociatedConstraintsSatisfaction) || !Info.AssociatedConstraintsSatisfaction.IsSatisfied) { Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs)); @@ -4572,8 +4574,11 @@ if (S.CheckTemplateArgumentList(Concept, SourceLocation(), TemplateArgs, /*PartialTemplateArgs=*/false, Converted)) return Sema::DAR_FailedAlreadyDiagnosed; + + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(Converted); if (S.CheckConstraintSatisfaction(Concept, {Concept->getConstraintExpr()}, - Converted, TypeLoc.getLocalSourceRange(), + MLTAL, TypeLoc.getLocalSourceRange(), Satisfaction)) return Sema::DAR_FailedAlreadyDiagnosed; if (!Satisfaction.IsSatisfied) { 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 IncludeContainingStructArgs) { // 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((IncludeContainingStructArgs || + Result.getNumSubstitutedLevels() == 0) && "Outer template not instantiated?"); } @@ -174,10 +177,18 @@ } } else if (const auto *Rec = dyn_cast(Ctx)) { if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) { - assert(Result.getNumSubstitutedLevels() == 0 && + assert((IncludeContainingStructArgs || + Result.getNumSubstitutedLevels() == 0) && "Outer template not instantiated?"); if (ClassTemplate->isMemberSpecialization()) break; + if (IncludeContainingStructArgs) { + QualType RecordType = Context.getTypeDeclType(Rec); + QualType Injected = cast(RecordType) + ->getInjectedSpecializationType(); + const auto *InjectedType = cast(Injected); + Result.addOuterTemplateArguments(InjectedType->template_arguments()); + } } } @@ -2304,6 +2315,17 @@ const MultiLevelTemplateArgumentList &TemplateArgs) { const ASTTemplateArgumentListInfo *TemplArgInfo = TC->getTemplateArgsAsWritten(); + + // If we're not checking a constraint, we shouldn't be instantiating the type + // constraint, so we should just create a copy of the previous one. + if (!IsEvaluatingAConstraint()) { + Inst->setTypeConstraint(TC->getNestedNameSpecifierLoc(), + TC->getConceptNameInfo(), TC->getNamedConcept(), + TC->getNamedConcept(), TemplArgInfo, + TC->getImmediatelyDeclaredConstraint()); + return false; + } + TemplateArgumentListInfo InstArgs; if (TemplArgInfo) { @@ -3488,6 +3510,14 @@ return Instantiator.TransformExpr(E); } +ExprResult +Sema::SubstConstraintExpr(Expr *E, + const MultiLevelTemplateArgumentList &TemplateArgs) { + + ConstraintEvalRAII EvalRAII(*this); + return SubstExpr(E, TemplateArgs); +} + ExprResult Sema::SubstInitializer(Expr *Init, const MultiLevelTemplateArgumentList &TemplateArgs, bool CXXDirectInit) { Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2032,19 +2032,7 @@ return nullptr; } - // 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()) - return nullptr; - TrailingRequiresClause = SubstRC.get(); - 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 @@ -2389,22 +2377,7 @@ return nullptr; } - // FIXME: Concepts: Do not substitute into constraint expressions Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); - if (TrailingRequiresClause) { - EnterExpressionEvaluationContext ConstantEvaluated( - SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); - auto *ThisContext = dyn_cast_or_null(Owner); - Sema::CXXThisScopeRAII ThisScope(SemaRef, ThisContext, - D->getMethodQualifiers(), ThisContext); - ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, - TemplateArgs); - if (SubstRC.isInvalid()) - return nullptr; - TrailingRequiresClause = SubstRC.get(); - if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause)) - return nullptr; - } DeclContext *DC = Owner; if (isFriend) { @@ -3981,18 +3954,7 @@ if (Invalid) return nullptr; - // FIXME: Concepts: Substitution into requires clause should only happen when - // 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; - } - InstRequiresClause = Res.get(); - } + Expr *InstRequiresClause = L->getRequiresClause(); TemplateParameterList *InstL = TemplateParameterList::Create(SemaRef.Context, L->getTemplateLoc(), Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -12920,11 +12920,8 @@ } // Transform the trailing requires clause - ExprResult NewTrailingRequiresClause; - if (Expr *TRC = E->getCallOperator()->getTrailingRequiresClause()) - // FIXME: Concepts: Substitution into requires clause should only happen - // when checking satisfaction. - NewTrailingRequiresClause = getDerived().TransformExpr(TRC); + ExprResult NewTrailingRequiresClause = + E->getCallOperator()->getTrailingRequiresClause(); // 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,23 @@ 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}} + +// Test the delayed instantiation, the 'foo' implementation shouldn't cause the +// constraint failure(or crash!) until the use to create 'y'. +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 @@ -40,6 +40,18 @@ static_assert(S::f(1)); +// Similar to the 'S' test, but tries to use 'U' in the requires clause. +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}} +}