forked from OSchip/llvm-project
[Concepts] Transform constraints of non-template functions to ConstantEvaluated
We would previously try to evaluate atomic constraints of non-template functions as-is, and since they are now unevaluated at first, this would cause incorrect evaluation (bugs #44657, #44656). Substitute into atomic constraints of non-template functions as we would atomic constraints of template functions, in order to rebuild the expressions in a constant-evaluated context.
This commit is contained in:
parent
3daa71ee00
commit
713562f548
|
@ -29,14 +29,14 @@ class ConceptSpecializationExpr;
|
|||
class ConstraintSatisfaction : public llvm::FoldingSetNode {
|
||||
// The template-like entity that 'owns' the constraint checked here (can be a
|
||||
// constrained entity or a concept).
|
||||
NamedDecl *ConstraintOwner = nullptr;
|
||||
const NamedDecl *ConstraintOwner = nullptr;
|
||||
llvm::SmallVector<TemplateArgument, 4> TemplateArgs;
|
||||
|
||||
public:
|
||||
|
||||
ConstraintSatisfaction() = default;
|
||||
|
||||
ConstraintSatisfaction(NamedDecl *ConstraintOwner,
|
||||
ConstraintSatisfaction(const NamedDecl *ConstraintOwner,
|
||||
ArrayRef<TemplateArgument> TemplateArgs) :
|
||||
ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs.begin(),
|
||||
TemplateArgs.end()) { }
|
||||
|
@ -57,7 +57,7 @@ public:
|
|||
}
|
||||
|
||||
static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C,
|
||||
NamedDecl *ConstraintOwner,
|
||||
const NamedDecl *ConstraintOwner,
|
||||
ArrayRef<TemplateArgument> TemplateArgs);
|
||||
};
|
||||
|
||||
|
|
|
@ -4686,6 +4686,8 @@ def note_checking_constraints_for_var_spec_id_here : Note<
|
|||
def note_checking_constraints_for_class_spec_id_here : Note<
|
||||
"while checking constraint satisfaction for class template partial "
|
||||
"specialization '%0' required here">;
|
||||
def note_checking_constraints_for_function_here : Note<
|
||||
"while checking constraint satisfaction for function '%0' required here">;
|
||||
def note_constraint_substitution_here : Note<
|
||||
"while substituting template arguments into constraint expression here">;
|
||||
def note_constraint_normalization_here : Note<
|
||||
|
|
|
@ -6288,7 +6288,7 @@ public:
|
|||
/// \returns true if an error occurred and satisfaction could not be checked,
|
||||
/// false otherwise.
|
||||
bool CheckConstraintSatisfaction(
|
||||
NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
|
||||
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
|
||||
ArrayRef<TemplateArgument> TemplateArgs,
|
||||
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction);
|
||||
|
||||
|
@ -6301,6 +6301,17 @@ public:
|
|||
bool CheckConstraintSatisfaction(const Expr *ConstraintExpr,
|
||||
ConstraintSatisfaction &Satisfaction);
|
||||
|
||||
/// Check whether the given function decl's trailing requires clause is
|
||||
/// satisfied, if any. Returns false and updates Satisfaction with the
|
||||
/// satisfaction verdict if successful, emits a diagnostic and returns true if
|
||||
/// an error occured and satisfaction could not be determined.
|
||||
///
|
||||
/// \returns true if an error occurred, false otherwise.
|
||||
bool CheckFunctionConstraints(const FunctionDecl *FD,
|
||||
ConstraintSatisfaction &Satisfaction,
|
||||
SourceLocation UsageLoc = SourceLocation());
|
||||
|
||||
|
||||
/// \brief Ensure that the given template arguments satisfy the constraints
|
||||
/// associated with the given template, emitting a diagnostic if they do not.
|
||||
///
|
||||
|
|
|
@ -59,8 +59,8 @@ ASTConstraintSatisfaction::Create(const ASTContext &C,
|
|||
}
|
||||
|
||||
void ConstraintSatisfaction::Profile(
|
||||
llvm::FoldingSetNodeID &ID, const ASTContext &C, NamedDecl *ConstraintOwner,
|
||||
ArrayRef<TemplateArgument> TemplateArgs) {
|
||||
llvm::FoldingSetNodeID &ID, const ASTContext &C,
|
||||
const NamedDecl *ConstraintOwner, ArrayRef<TemplateArgument> TemplateArgs) {
|
||||
ID.AddPointer(ConstraintOwner);
|
||||
ID.AddInteger(TemplateArgs.size());
|
||||
for (auto &Arg : TemplateArgs)
|
||||
|
|
|
@ -167,9 +167,8 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
|
|||
return false;
|
||||
}
|
||||
|
||||
template <typename TemplateDeclT>
|
||||
static bool calculateConstraintSatisfaction(
|
||||
Sema &S, TemplateDeclT *Template, ArrayRef<TemplateArgument> TemplateArgs,
|
||||
Sema &S, const NamedDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
|
||||
SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL,
|
||||
const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) {
|
||||
return calculateConstraintSatisfaction(
|
||||
|
@ -182,8 +181,9 @@ static bool calculateConstraintSatisfaction(
|
|||
{
|
||||
TemplateDeductionInfo Info(TemplateNameLoc);
|
||||
Sema::InstantiatingTemplate Inst(S, AtomicExpr->getBeginLoc(),
|
||||
Sema::InstantiatingTemplate::ConstraintSubstitution{}, Template,
|
||||
Info, AtomicExpr->getSourceRange());
|
||||
Sema::InstantiatingTemplate::ConstraintSubstitution{},
|
||||
const_cast<NamedDecl *>(Template), Info,
|
||||
AtomicExpr->getSourceRange());
|
||||
if (Inst.isInvalid())
|
||||
return ExprError();
|
||||
// We do not want error diagnostics escaping here.
|
||||
|
@ -230,8 +230,7 @@ static bool calculateConstraintSatisfaction(
|
|||
});
|
||||
}
|
||||
|
||||
template<typename TemplateDeclT>
|
||||
static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,
|
||||
static bool CheckConstraintSatisfaction(Sema &S, const NamedDecl *Template,
|
||||
ArrayRef<const Expr *> ConstraintExprs,
|
||||
ArrayRef<TemplateArgument> TemplateArgs,
|
||||
SourceRange TemplateIDRange,
|
||||
|
@ -249,8 +248,8 @@ static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,
|
|||
}
|
||||
|
||||
Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
|
||||
Sema::InstantiatingTemplate::ConstraintsCheck{}, Template, TemplateArgs,
|
||||
TemplateIDRange);
|
||||
Sema::InstantiatingTemplate::ConstraintsCheck{},
|
||||
const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange);
|
||||
if (Inst.isInvalid())
|
||||
return true;
|
||||
|
||||
|
@ -273,7 +272,7 @@ static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,
|
|||
}
|
||||
|
||||
bool Sema::CheckConstraintSatisfaction(
|
||||
NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
|
||||
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
|
||||
ArrayRef<TemplateArgument> TemplateArgs, SourceRange TemplateIDRange,
|
||||
ConstraintSatisfaction &OutSatisfaction) {
|
||||
if (ConstraintExprs.empty()) {
|
||||
|
@ -284,7 +283,8 @@ bool Sema::CheckConstraintSatisfaction(
|
|||
llvm::FoldingSetNodeID ID;
|
||||
void *InsertPos;
|
||||
ConstraintSatisfaction *Satisfaction = nullptr;
|
||||
if (LangOpts.ConceptSatisfactionCaching) {
|
||||
bool ShouldCache = LangOpts.ConceptSatisfactionCaching && Template;
|
||||
if (ShouldCache) {
|
||||
ConstraintSatisfaction::Profile(ID, Context, Template, TemplateArgs);
|
||||
Satisfaction = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos);
|
||||
if (Satisfaction) {
|
||||
|
@ -295,27 +295,15 @@ bool Sema::CheckConstraintSatisfaction(
|
|||
} 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)
|
||||
if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs,
|
||||
TemplateArgs, TemplateIDRange,
|
||||
*Satisfaction)) {
|
||||
if (ShouldCache)
|
||||
delete Satisfaction;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LangOpts.ConceptSatisfactionCaching) {
|
||||
if (ShouldCache) {
|
||||
// We cannot use InsertNode here because CheckConstraintSatisfaction might
|
||||
// have invalidated it.
|
||||
SatisfactionCache.InsertNode(Satisfaction);
|
||||
|
@ -333,6 +321,22 @@ bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
|
|||
});
|
||||
}
|
||||
|
||||
bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
|
||||
ConstraintSatisfaction &Satisfaction,
|
||||
SourceLocation UsageLoc) {
|
||||
const Expr *RC = FD->getTrailingRequiresClause();
|
||||
assert(!RC->isInstantiationDependent() &&
|
||||
"CheckFunctionConstraints can only be used with functions with "
|
||||
"non-dependent constraints");
|
||||
// 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=*/{},
|
||||
SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
|
||||
Satisfaction);
|
||||
}
|
||||
|
||||
bool Sema::EnsureTemplateArgumentListConstraints(
|
||||
TemplateDecl *TD, ArrayRef<TemplateArgument> TemplateArgs,
|
||||
SourceRange TemplateIDRange) {
|
||||
|
|
|
@ -335,7 +335,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
|
|||
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
||||
if (Expr *RC = FD->getTrailingRequiresClause()) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
bool Failed = CheckConstraintSatisfaction(RC, Satisfaction);
|
||||
bool Failed = CheckConstraintSatisfaction(FD, {RC}, /*TemplateArgs=*/{},
|
||||
SourceRange(Loc), Satisfaction);
|
||||
if (Failed)
|
||||
// A diagnostic will have already been generated (non-constant
|
||||
// constraint expression, for example)
|
||||
|
|
|
@ -8497,7 +8497,8 @@ concepts::NestedRequirement *
|
|||
Sema::BuildNestedRequirement(Expr *Constraint) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (!Constraint->isInstantiationDependent() &&
|
||||
CheckConstraintSatisfaction(Constraint, Satisfaction))
|
||||
CheckConstraintSatisfaction(nullptr, {Constraint}, /*TemplateArgs=*/{},
|
||||
Constraint->getSourceRange(), Satisfaction))
|
||||
return nullptr;
|
||||
return new (Context) concepts::NestedRequirement(Context, Constraint,
|
||||
Satisfaction);
|
||||
|
|
|
@ -6295,9 +6295,9 @@ void Sema::AddOverloadCandidate(
|
|||
return;
|
||||
}
|
||||
|
||||
if (Expr *RequiresClause = Function->getTrailingRequiresClause()) {
|
||||
if (Function->getTrailingRequiresClause()) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) ||
|
||||
if (CheckFunctionConstraints(Function, Satisfaction) ||
|
||||
!Satisfaction.IsSatisfied) {
|
||||
Candidate.Viable = false;
|
||||
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
|
||||
|
@ -6812,9 +6812,9 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
|
|||
return;
|
||||
}
|
||||
|
||||
if (Expr *RequiresClause = Method->getTrailingRequiresClause()) {
|
||||
if (Method->getTrailingRequiresClause()) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) ||
|
||||
if (CheckFunctionConstraints(Method, Satisfaction) ||
|
||||
!Satisfaction.IsSatisfied) {
|
||||
Candidate.Viable = false;
|
||||
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
|
||||
|
@ -7208,10 +7208,9 @@ void Sema::AddConversionCandidate(
|
|||
return;
|
||||
}
|
||||
|
||||
Expr *RequiresClause = Conversion->getTrailingRequiresClause();
|
||||
if (RequiresClause) {
|
||||
if (Conversion->getTrailingRequiresClause()) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) ||
|
||||
if (CheckFunctionConstraints(Conversion, Satisfaction) ||
|
||||
!Satisfaction.IsSatisfied) {
|
||||
Candidate.Viable = false;
|
||||
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
|
||||
|
@ -9951,9 +9950,9 @@ static bool checkAddressOfFunctionIsAvailable(Sema &S, const FunctionDecl *FD,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (const Expr *RC = FD->getTrailingRequiresClause()) {
|
||||
if (FD->getTrailingRequiresClause()) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (S.CheckConstraintSatisfaction(RC, Satisfaction))
|
||||
if (S.CheckFunctionConstraints(FD, Satisfaction, Loc))
|
||||
return false;
|
||||
if (!Satisfaction.IsSatisfied) {
|
||||
if (Complain) {
|
||||
|
@ -10978,8 +10977,7 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
|
|||
<< (unsigned)FnKindPair.first << (unsigned)ocs_non_template
|
||||
<< FnDesc /* Ignored */;
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (S.CheckConstraintSatisfaction(Fn->getTrailingRequiresClause(),
|
||||
Satisfaction))
|
||||
if (S.CheckFunctionConstraints(Fn, Satisfaction))
|
||||
break;
|
||||
S.DiagnoseUnsatisfiedConstraint(Satisfaction);
|
||||
}
|
||||
|
|
|
@ -763,21 +763,30 @@ void Sema::PrintInstantiationStack() {
|
|||
|
||||
case CodeSynthesisContext::ConstraintsCheck: {
|
||||
unsigned DiagID = 0;
|
||||
if (!Active->Entity) {
|
||||
Diags.Report(Active->PointOfInstantiation,
|
||||
diag::note_nested_requirement_here)
|
||||
<< Active->InstantiationRange;
|
||||
break;
|
||||
}
|
||||
if (isa<ConceptDecl>(Active->Entity))
|
||||
DiagID = diag::note_concept_specialization_here;
|
||||
else if (isa<TemplateDecl>(Active->Entity))
|
||||
DiagID = diag::note_checking_constraints_for_template_id_here;
|
||||
else if (isa<VarTemplatePartialSpecializationDecl>(Active->Entity))
|
||||
DiagID = diag::note_checking_constraints_for_var_spec_id_here;
|
||||
else {
|
||||
assert(isa<ClassTemplatePartialSpecializationDecl>(Active->Entity));
|
||||
else if (isa<ClassTemplatePartialSpecializationDecl>(Active->Entity))
|
||||
DiagID = diag::note_checking_constraints_for_class_spec_id_here;
|
||||
else {
|
||||
assert(isa<FunctionDecl>(Active->Entity));
|
||||
DiagID = diag::note_checking_constraints_for_function_here;
|
||||
}
|
||||
SmallVector<char, 128> TemplateArgsStr;
|
||||
llvm::raw_svector_ostream OS(TemplateArgsStr);
|
||||
cast<NamedDecl>(Active->Entity)->printName(OS);
|
||||
printTemplateArgumentList(OS, Active->template_arguments(),
|
||||
getPrintingPolicy());
|
||||
if (!isa<FunctionDecl>(Active->Entity))
|
||||
printTemplateArgumentList(OS, Active->template_arguments(),
|
||||
getPrintingPolicy());
|
||||
Diags.Report(Active->PointOfInstantiation, DiagID) << OS.str()
|
||||
<< Active->InstantiationRange;
|
||||
break;
|
||||
|
|
|
@ -3,15 +3,51 @@
|
|||
// Make sure constraint expressions are unevaluated before being substituted
|
||||
// into during satisfaction checking.
|
||||
|
||||
template<typename T> constexpr int f() { return T::value; }
|
||||
template<typename T> concept Foo = false && (f<int>(), true);
|
||||
bool k = Foo<int>;
|
||||
template<typename T> requires false && (f<int>(), true) struct S {};
|
||||
// expected-note@-1{{because}}
|
||||
using s = S<int>; // expected-error {{constraints not satisfied}}
|
||||
template<typename T> void foo() requires false && (f<int>(), true) { };
|
||||
// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}}
|
||||
int a = (foo<int>(), 0); // expected-error{{no matching function}}
|
||||
template<typename T> void bar() requires requires { requires false && (f<int>(), true); } { };
|
||||
// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}}
|
||||
int b = (bar<int>(), 0); // expected-error{{no matching function}}
|
||||
template<typename T> constexpr bool f = T::value;
|
||||
// expected-error@-1 4{{type}}
|
||||
|
||||
namespace unevaluated {
|
||||
template<typename T> concept Foo = false && f<int>;
|
||||
bool k = Foo<int>;
|
||||
template<typename T> requires false && f<int> struct S {};
|
||||
// expected-note@-1{{because}}
|
||||
using s = S<int>; // expected-error {{constraints not satisfied}}
|
||||
template<typename T> void foo() requires false && f<int> { };
|
||||
// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}}
|
||||
int a = (foo<int>(), 0); // expected-error{{no matching function}}
|
||||
template<typename T> void bar() requires requires { requires false && f<int>; } { };
|
||||
// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}}
|
||||
int b = (bar<int>(), 0); // expected-error{{no matching function}}
|
||||
template<typename T> struct M { static void foo() requires false && f<int> { }; };
|
||||
// expected-note@-1{{because}}
|
||||
int c = (M<int>::foo(), 0);
|
||||
// expected-error@-1{{invalid reference to function 'foo': constraints not satisfied}}
|
||||
}
|
||||
|
||||
namespace constant_evaluated {
|
||||
template<typename T> requires f<int[0]> struct S {};
|
||||
// expected-note@-1{{in instantiation of}} expected-note@-1{{while substituting}} \
|
||||
expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}} \
|
||||
expected-note@-1{{subexpression not valid}}
|
||||
using s = S<int>;
|
||||
// expected-note@-1 2{{while checking}}
|
||||
template<typename T> void foo() requires f<int[1]> { };
|
||||
// expected-note@-1{{in instantiation}} expected-note@-1{{while substituting}} \
|
||||
expected-note@-1{{candidate template ignored}} expected-note@-1{{subexpression not valid}} \
|
||||
expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}}
|
||||
int a = (foo<int>(), 0);
|
||||
// expected-note@-1 2{{while checking}} expected-error@-1{{no matching function}} \
|
||||
expected-note@-1 2{{in instantiation}}
|
||||
template<typename T> void bar() requires requires { requires f<int[2]>; } { };
|
||||
// expected-note@-1{{in instantiation}} expected-note@-1{{subexpression not valid}} \
|
||||
expected-note@-1{{while substituting}} \
|
||||
expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}} \
|
||||
expected-note@-1 2{{while checking the satisfaction of nested requirement}}
|
||||
int b = (bar<int>(), 0);
|
||||
template<typename T> struct M { static void foo() requires f<int[3]> { }; };
|
||||
// expected-note@-1{{in instantiation}} expected-note@-1{{subexpression not valid}} \
|
||||
expected-note@-1{{while substituting}} \
|
||||
expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}}
|
||||
int c = (M<int>::foo(), 0);
|
||||
// expected-note@-1 2{{while checking}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue