Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Clang] Reapply CWG2369 "Ordering between constraints and substitution" #122423

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -13062,6 +13062,7 @@ class Sema final : public SemaBase {
///
/// \param SkipForSpecialization when specified, any template specializations
/// in a traversal would be ignored.
///
/// \param ForDefaultArgumentSubstitution indicates we should continue looking
/// when encountering a specialized member function template, rather than
/// returning immediately.
Expand All @@ -13073,6 +13074,17 @@ class Sema final : public SemaBase {
bool SkipForSpecialization = false,
bool ForDefaultArgumentSubstitution = false);

/// Apart from storing the result to \p Result, this behaves the same as
/// another overload.
void getTemplateInstantiationArgs(
MultiLevelTemplateArgumentList &Result, const NamedDecl *D,
const DeclContext *DC = nullptr, bool Final = false,
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
bool ForConstraintInstantiation = false,
bool SkipForSpecialization = false,
bool ForDefaultArgumentSubstitution = false);

/// RAII object to handle the state changes required to synthesize
/// a function body.
class SynthesizedFunctionScope {
Expand Down Expand Up @@ -13169,6 +13181,10 @@ class Sema final : public SemaBase {
// FIXME: Should we have a similar limit for other forms of synthesis?
unsigned NonInstantiationEntries;

/// The number of \p CodeSynthesisContexts that are not constraint
/// substitution.
unsigned NonConstraintSubstitutionEntries;

/// The depth of the context stack at the point when the most recent
/// error or warning was produced.
///
Expand Down Expand Up @@ -13342,7 +13358,7 @@ class Sema final : public SemaBase {
ExprResult
SubstConstraintExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs);
// Unlike the above, this does not evaluates constraints.
// Unlike the above, this does not evaluate constraints.
ExprResult SubstConstraintExprWithoutSatisfaction(
Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs);

Expand Down Expand Up @@ -13491,6 +13507,11 @@ class Sema final : public SemaBase {
return CodeSynthesisContexts.size() > NonInstantiationEntries;
}

/// Determine whether we are currently performing constraint substitution.
bool inConstraintSubstitution() const {
return CodeSynthesisContexts.size() > NonConstraintSubstitutionEntries;
}

using EntityPrinter = llvm::function_ref<void(llvm::raw_ostream &)>;

/// \brief create a Requirement::SubstitutionDiagnostic with only a
Expand Down Expand Up @@ -14463,10 +14484,10 @@ class Sema final : public SemaBase {
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceRange TemplateIDRange);

bool CheckInstantiatedFunctionTemplateConstraints(
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
ArrayRef<TemplateArgument> TemplateArgs,
ConstraintSatisfaction &Satisfaction);
bool CheckFunctionTemplateConstraints(SourceLocation PointOfInstantiation,
FunctionDecl *Decl,
ArrayRef<TemplateArgument> TemplateArgs,
ConstraintSatisfaction &Satisfaction);

/// \brief Emit diagnostics explaining why a constraint expression was deemed
/// unsatisfied.
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Sema/Template.h
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,12 @@ enum class TemplateSubstitutionKind : char {
llvm::PointerUnion<Decl *, DeclArgumentPack *> *
findInstantiationOf(const Decl *D);

/// Similar to \p findInstantiationOf(), but it wouldn't assert if the
/// instantiation was not found within the current instantiation scope. This
/// is helpful for on-demand declaration instantiation.
llvm::PointerUnion<Decl *, DeclArgumentPack *> *
findInstantiationUnsafe(const Decl *D);

void InstantiatedLocal(const Decl *D, Decl *Inst);
void InstantiatedLocalPackArg(const Decl *D, VarDecl *Inst);
void MakeInstantiatedLocalArgPack(const Decl *D);
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
TyposCorrected(0), IsBuildingRecoveryCallExpr(false), NumSFINAEErrors(0),
AccessCheckingSFINAE(false), CurrentInstantiationScope(nullptr),
InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0),
ArgumentPackSubstitutionIndex(-1), SatisfactionCache(Context) {
NonConstraintSubstitutionEntries(0), ArgumentPackSubstitutionIndex(-1),
SatisfactionCache(Context) {
assert(pp.TUKind == TUKind);
TUScope = nullptr;

Expand Down
50 changes: 48 additions & 2 deletions clang/lib/Sema/SemaConcept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,9 @@ static ExprResult calculateConstraintSatisfaction(
ConstraintSatisfaction &Satisfaction;

ExprResult EvaluateAtomicConstraint(const Expr *AtomicExpr) const {
EnterExpressionEvaluationContext UnevaluatedSubstitution(
S, Sema::ExpressionEvaluationContext::Unevaluated,
Sema::ReuseLambdaContextDecl);
EnterExpressionEvaluationContext ConstantEvaluated(
S, Sema::ExpressionEvaluationContext::ConstantEvaluated,
Sema::ReuseLambdaContextDecl);
Expand Down Expand Up @@ -846,7 +849,7 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
bool ForOverloadResolution) {
// 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
// CheckFunctionTemplateConstraints after this will check it
// better.
if (FD->isDependentContext() ||
FD->getTemplatedKind() ==
Expand Down Expand Up @@ -1111,12 +1114,55 @@ bool Sema::EnsureTemplateArgumentListConstraints(
return false;
}

bool Sema::CheckInstantiatedFunctionTemplateConstraints(
static bool CheckFunctionConstraintsWithoutInstantiation(
Sema &SemaRef, SourceLocation PointOfInstantiation,
FunctionTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
ConstraintSatisfaction &Satisfaction) {
SmallVector<const Expr *, 3> TemplateAC;
Template->getAssociatedConstraints(TemplateAC);
if (TemplateAC.empty()) {
Satisfaction.IsSatisfied = true;
return false;
}

LocalInstantiationScope Scope(SemaRef);

FunctionDecl *FD = Template->getTemplatedDecl();
// Collect the list of template arguments relative to the 'primary'
// template. We need the entire list, since the constraint is completely
// uninstantiated at this point.

// FIXME: Add TemplateArgs through the 'Innermost' parameter once
// the refactoring of getTemplateInstantiationArgs() relands.
MultiLevelTemplateArgumentList MLTAL;
MLTAL.addOuterTemplateArguments(Template, std::nullopt, /*Final=*/false);
SemaRef.getTemplateInstantiationArgs(
MLTAL, /*D=*/FD, FD,
/*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true,
/*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true);
MLTAL.replaceInnermostTemplateArguments(Template, TemplateArgs);

Sema::ContextRAII SavedContext(SemaRef, FD);
std::optional<Sema::CXXThisScopeRAII> ThisScope;
if (auto *Method = dyn_cast<CXXMethodDecl>(FD))
ThisScope.emplace(SemaRef, /*Record=*/Method->getParent(),
/*ThisQuals=*/Method->getMethodQualifiers());
return SemaRef.CheckConstraintSatisfaction(
Template, TemplateAC, MLTAL, PointOfInstantiation, Satisfaction);
}

bool Sema::CheckFunctionTemplateConstraints(
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
ArrayRef<TemplateArgument> TemplateArgs,
ConstraintSatisfaction &Satisfaction) {
// In most cases we're not going to have constraints, so check for that first.
FunctionTemplateDecl *Template = Decl->getPrimaryTemplate();

if (!Template)
return ::CheckFunctionConstraintsWithoutInstantiation(
*this, PointOfInstantiation, Decl->getDescribedFunctionTemplate(),
TemplateArgs, Satisfaction);

// Note - code synthesis context for the constraints check is created
// inside CheckConstraintsSatisfaction.
SmallVector<const Expr *, 3> TemplateAC;
Expand Down
56 changes: 39 additions & 17 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3936,18 +3936,6 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
Result != TemplateDeductionResult::Success)
return Result;

// C++ [temp.deduct.call]p10: [DR1391]
// If deduction succeeds for all parameters that contain
// template-parameters that participate in template argument deduction,
// and all template arguments are explicitly specified, deduced, or
// obtained from default template arguments, remaining parameters are then
// compared with the corresponding arguments. For each remaining parameter
// P with a type that was non-dependent before substitution of any
// explicitly-specified template arguments, if the corresponding argument
// A cannot be implicitly converted to P, deduction fails.
if (CheckNonDependent())
return TemplateDeductionResult::NonDependentConversionFailure;

// Form the template argument list from the deduced template arguments.
TemplateArgumentList *SugaredDeducedArgumentList =
TemplateArgumentList::CreateCopy(Context, SugaredBuilder);
Expand Down Expand Up @@ -3977,6 +3965,39 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
FD = const_cast<FunctionDecl *>(FDFriend);
Owner = FD->getLexicalDeclContext();
}
// C++20 [temp.deduct.general]p5: [CWG2369]
// If the function template has associated constraints, those constraints
// are checked for satisfaction. If the constraints are not satisfied, type
// deduction fails.
//
// FIXME: We haven't implemented CWG2369 for lambdas yet, because we need
// to figure out how to instantiate lambda captures to the scope without
// first instantiating the lambda.
bool IsLambda = isLambdaCallOperator(FD) || isLambdaConversionOperator(FD);
if (!IsLambda && !IsIncomplete) {
if (CheckFunctionTemplateConstraints(
Info.getLocation(),
FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(),
CanonicalBuilder, Info.AssociatedConstraintsSatisfaction))
return TemplateDeductionResult::MiscellaneousDeductionFailure;
if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
Info.reset(Info.takeSugared(),
TemplateArgumentList::CreateCopy(Context, CanonicalBuilder));
return TemplateDeductionResult::ConstraintsNotSatisfied;
}
}
// C++ [temp.deduct.call]p10: [CWG1391]
// If deduction succeeds for all parameters that contain
// template-parameters that participate in template argument deduction,
// and all template arguments are explicitly specified, deduced, or
// obtained from default template arguments, remaining parameters are then
// compared with the corresponding arguments. For each remaining parameter
// P with a type that was non-dependent before substitution of any
// explicitly-specified template arguments, if the corresponding argument
// A cannot be implicitly converted to P, deduction fails.
if (CheckNonDependent())
return TemplateDeductionResult::NonDependentConversionFailure;

MultiLevelTemplateArgumentList SubstArgs(
FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
/*Final=*/false);
Expand Down Expand Up @@ -4011,8 +4032,8 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
// ([temp.constr.decl]), those constraints are checked for satisfaction
// ([temp.constr.constr]). If the constraints are not satisfied, type
// deduction fails.
if (!IsIncomplete) {
if (CheckInstantiatedFunctionTemplateConstraints(
if (IsLambda && !IsIncomplete) {
if (CheckFunctionTemplateConstraints(
Info.getLocation(), Specialization, CanonicalBuilder,
Info.AssociatedConstraintsSatisfaction))
return TemplateDeductionResult::MiscellaneousDeductionFailure;
Expand Down Expand Up @@ -4761,8 +4782,8 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
/*AdjustExceptionSpec*/false);

// Unevaluated SFINAE context.
EnterExpressionEvaluationContext Unevaluated(
*this, Sema::ExpressionEvaluationContext::Unevaluated);
std::optional<EnterExpressionEvaluationContext> Unevaluated(
std::in_place, *this, Sema::ExpressionEvaluationContext::Unevaluated);
SFINAETrap Trap(*this);

Deduced.resize(TemplateParams->size());
Expand Down Expand Up @@ -4804,13 +4825,14 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
DeduceReturnType(Specialization, Info.getLocation(), false))
return TemplateDeductionResult::MiscellaneousDeductionFailure;

Unevaluated = std::nullopt;
// [C++26][expr.const]/p17
// An expression or conversion is immediate-escalating if it is not initially
// in an immediate function context and it is [...]
// a potentially-evaluated id-expression that denotes an immediate function.
if (IsAddressOfFunction && getLangOpts().CPlusPlus20 &&
Specialization->isImmediateEscalating() &&
parentEvaluationContext().isPotentiallyEvaluated() &&
currentEvaluationContext().isPotentiallyEvaluated() &&
CheckIfFunctionSpecializationIsImmediate(Specialization,
Info.getLocation()))
return TemplateDeductionResult::MiscellaneousDeductionFailure;
Expand Down
8 changes: 5 additions & 3 deletions clang/lib/Sema/SemaTemplateDeductionGuide.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -902,10 +902,12 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef,
Context.getTrivialTypeSourceInfo(
Context.getDeducedTemplateSpecializationType(
TemplateName(AliasTemplate), /*DeducedType=*/QualType(),
/*IsDependent=*/true)), // template specialization type whose
// arguments will be deduced.
/*IsDependent=*/true),
AliasTemplate->getLocation()), // template specialization type whose
// arguments will be deduced.
Context.getTrivialTypeSourceInfo(
ReturnType), // type from which template arguments are deduced.
ReturnType, AliasTemplate->getLocation()), // type from which template
// arguments are deduced.
};
return TypeTraitExpr::Create(
Context, Context.getLogicalOperationType(), AliasTemplate->getLocation(),
Expand Down
Loading
Loading