Skip to content

Commit e04e140

Browse files
authored
[Clang] Reapply CWG2369 "Ordering between constraints and substitution" (#122423)
The previous approach broke code generation for the MS ABI due to an unintended code path during constraint substitution. This time we address the issue by inspecting the evaluation contexts and thereby avoiding that code path. This reapplies 96eced6 (#102857).
1 parent e3a0cb8 commit e04e140

27 files changed

+729
-91
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10505,13 +10505,34 @@ class Sema final : public SemaBase {
1050510505
OverloadCandidateParamOrder PO = {},
1050610506
bool AggregateCandidateDeduction = false);
1050710507

10508+
struct CheckNonDependentConversionsFlag {
10509+
/// Do not consider any user-defined conversions when constructing the
10510+
/// initializing sequence.
10511+
bool SuppressUserConversions;
10512+
10513+
/// Before constructing the initializing sequence, we check whether the
10514+
/// parameter type and argument type contain any user defined conversions.
10515+
/// If so, do not initialize them. This effectively bypasses some undesired
10516+
/// instantiation before checking constaints, which might otherwise result
10517+
/// in non-SFINAE errors e.g. recursive constraints.
10518+
bool OnlyInitializeNonUserDefinedConversions;
10519+
10520+
CheckNonDependentConversionsFlag(
10521+
bool SuppressUserConversions,
10522+
bool OnlyInitializeNonUserDefinedConversions)
10523+
: SuppressUserConversions(SuppressUserConversions),
10524+
OnlyInitializeNonUserDefinedConversions(
10525+
OnlyInitializeNonUserDefinedConversions) {}
10526+
};
10527+
1050810528
/// Check that implicit conversion sequences can be formed for each argument
1050910529
/// whose corresponding parameter has a non-dependent type, per DR1391's
1051010530
/// [temp.deduct.call]p10.
1051110531
bool CheckNonDependentConversions(
1051210532
FunctionTemplateDecl *FunctionTemplate, ArrayRef<QualType> ParamTypes,
1051310533
ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,
10514-
ConversionSequenceList &Conversions, bool SuppressUserConversions,
10534+
ConversionSequenceList &Conversions,
10535+
CheckNonDependentConversionsFlag UserConversionFlag,
1051510536
CXXRecordDecl *ActingContext = nullptr, QualType ObjectType = QualType(),
1051610537
Expr::Classification ObjectClassification = {},
1051710538
OverloadCandidateParamOrder PO = {});
@@ -12555,14 +12576,22 @@ class Sema final : public SemaBase {
1255512576
///
1255612577
/// \param OriginalCallArgs If non-NULL, the original call arguments against
1255712578
/// which the deduced argument types should be compared.
12579+
/// \param CheckNonDependent Callback before substituting into the declaration
12580+
/// with the deduced template arguments.
12581+
/// \param OnlyInitializeNonUserDefinedConversions is used as a workaround for
12582+
/// some breakages introduced by CWG2369, where non-user-defined conversions
12583+
/// are checked first before the constraints.
1255812584
TemplateDeductionResult FinishTemplateArgumentDeduction(
1255912585
FunctionTemplateDecl *FunctionTemplate,
1256012586
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
1256112587
unsigned NumExplicitlySpecified, FunctionDecl *&Specialization,
1256212588
sema::TemplateDeductionInfo &Info,
1256312589
SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs,
1256412590
bool PartialOverloading, bool PartialOrdering,
12565-
llvm::function_ref<bool()> CheckNonDependent = [] { return false; });
12591+
llvm::function_ref<bool(bool)> CheckNonDependent =
12592+
[](bool /*OnlyInitializeNonUserDefinedConversions*/) {
12593+
return false;
12594+
});
1256612595

1256712596
/// Perform template argument deduction from a function call
1256812597
/// (C++ [temp.deduct.call]).
@@ -12598,7 +12627,7 @@ class Sema final : public SemaBase {
1259812627
bool PartialOrdering, QualType ObjectType,
1259912628
Expr::Classification ObjectClassification,
1260012629
bool ForOverloadSetAddressResolution,
12601-
llvm::function_ref<bool(ArrayRef<QualType>)> CheckNonDependent);
12630+
llvm::function_ref<bool(ArrayRef<QualType>, bool)> CheckNonDependent);
1260212631

1260312632
/// Deduce template arguments when taking the address of a function
1260412633
/// template (C++ [temp.deduct.funcaddr]) or matching a specialization to
@@ -13074,6 +13103,9 @@ class Sema final : public SemaBase {
1307413103
/// Was the enclosing context a non-instantiation SFINAE context?
1307513104
bool SavedInNonInstantiationSFINAEContext;
1307613105

13106+
/// Whether we're substituting into constraints.
13107+
bool InConstraintSubstitution;
13108+
1307713109
/// The point of instantiation or synthesis within the source code.
1307813110
SourceLocation PointOfInstantiation;
1307913111

@@ -13123,9 +13155,9 @@ class Sema final : public SemaBase {
1312313155

1312413156
CodeSynthesisContext()
1312513157
: Kind(TemplateInstantiation),
13126-
SavedInNonInstantiationSFINAEContext(false), Entity(nullptr),
13127-
Template(nullptr), TemplateArgs(nullptr), NumTemplateArgs(0),
13128-
DeductionInfo(nullptr) {}
13158+
SavedInNonInstantiationSFINAEContext(false),
13159+
InConstraintSubstitution(false), Entity(nullptr), Template(nullptr),
13160+
TemplateArgs(nullptr), NumTemplateArgs(0), DeductionInfo(nullptr) {}
1312913161

1313013162
/// Determines whether this template is an actual instantiation
1313113163
/// that should be counted toward the maximum instantiation depth.
@@ -13369,9 +13401,22 @@ class Sema final : public SemaBase {
1336913401
///
1337013402
/// \param SkipForSpecialization when specified, any template specializations
1337113403
/// in a traversal would be ignored.
13404+
///
1337213405
/// \param ForDefaultArgumentSubstitution indicates we should continue looking
1337313406
/// when encountering a specialized member function template, rather than
1337413407
/// returning immediately.
13408+
void getTemplateInstantiationArgs(
13409+
MultiLevelTemplateArgumentList &Result, const NamedDecl *D,
13410+
const DeclContext *DC = nullptr, bool Final = false,
13411+
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
13412+
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
13413+
bool ForConstraintInstantiation = false,
13414+
bool SkipForSpecialization = false,
13415+
bool ForDefaultArgumentSubstitution = false);
13416+
13417+
/// This creates a new \p MultiLevelTemplateArgumentList and invokes the other
13418+
/// overload with it as the first parameter. Prefer this overload in most
13419+
/// situations.
1337513420
MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
1337613421
const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false,
1337713422
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
@@ -13644,7 +13689,7 @@ class Sema final : public SemaBase {
1364413689
ExprResult
1364513690
SubstConstraintExpr(Expr *E,
1364613691
const MultiLevelTemplateArgumentList &TemplateArgs);
13647-
// Unlike the above, this does not evaluates constraints.
13692+
// Unlike the above, this does not evaluate constraints.
1364813693
ExprResult SubstConstraintExprWithoutSatisfaction(
1364913694
Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs);
1365013695

@@ -13794,6 +13839,12 @@ class Sema final : public SemaBase {
1379413839
return CodeSynthesisContexts.size() > NonInstantiationEntries;
1379513840
}
1379613841

13842+
/// Determine whether we are currently performing constraint substitution.
13843+
bool inConstraintSubstitution() const {
13844+
return !CodeSynthesisContexts.empty() &&
13845+
CodeSynthesisContexts.back().InConstraintSubstitution;
13846+
}
13847+
1379713848
using EntityPrinter = llvm::function_ref<void(llvm::raw_ostream &)>;
1379813849

1379913850
/// \brief create a Requirement::SubstitutionDiagnostic with only a
@@ -14786,10 +14837,10 @@ class Sema final : public SemaBase {
1478614837
const MultiLevelTemplateArgumentList &TemplateArgs,
1478714838
SourceRange TemplateIDRange);
1478814839

14789-
bool CheckInstantiatedFunctionTemplateConstraints(
14790-
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
14791-
ArrayRef<TemplateArgument> TemplateArgs,
14792-
ConstraintSatisfaction &Satisfaction);
14840+
bool CheckFunctionTemplateConstraints(SourceLocation PointOfInstantiation,
14841+
FunctionDecl *Decl,
14842+
ArrayRef<TemplateArgument> TemplateArgs,
14843+
ConstraintSatisfaction &Satisfaction);
1479314844

1479414845
/// \brief Emit diagnostics explaining why a constraint expression was deemed
1479514846
/// unsatisfied.

clang/include/clang/Sema/Template.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,12 @@ enum class TemplateSubstitutionKind : char {
522522
llvm::PointerUnion<Decl *, DeclArgumentPack *> *
523523
findInstantiationOf(const Decl *D);
524524

525+
/// Similar to \p findInstantiationOf(), but it wouldn't assert if the
526+
/// instantiation was not found within the current instantiation scope. This
527+
/// is helpful for on-demand declaration instantiation.
528+
llvm::PointerUnion<Decl *, DeclArgumentPack *> *
529+
getInstantiationOfIfExists(const Decl *D);
530+
525531
void InstantiatedLocal(const Decl *D, Decl *Inst);
526532
void InstantiatedLocalPackArg(const Decl *D, VarDecl *Inst);
527533
void MakeInstantiatedLocalArgPack(const Decl *D);

clang/lib/Sema/SemaConcept.cpp

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,7 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
792792
bool ForOverloadResolution) {
793793
// Don't check constraints if the function is dependent. Also don't check if
794794
// this is a function template specialization, as the call to
795-
// CheckinstantiatedFunctionTemplateConstraints after this will check it
795+
// CheckFunctionTemplateConstraints after this will check it
796796
// better.
797797
if (FD->isDependentContext() ||
798798
FD->getTemplatedKind() ==
@@ -1060,12 +1060,55 @@ bool Sema::EnsureTemplateArgumentListConstraints(
10601060
return false;
10611061
}
10621062

1063-
bool Sema::CheckInstantiatedFunctionTemplateConstraints(
1063+
static bool CheckFunctionConstraintsWithoutInstantiation(
1064+
Sema &SemaRef, SourceLocation PointOfInstantiation,
1065+
FunctionTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
1066+
ConstraintSatisfaction &Satisfaction) {
1067+
SmallVector<AssociatedConstraint, 3> TemplateAC;
1068+
Template->getAssociatedConstraints(TemplateAC);
1069+
if (TemplateAC.empty()) {
1070+
Satisfaction.IsSatisfied = true;
1071+
return false;
1072+
}
1073+
1074+
LocalInstantiationScope Scope(SemaRef);
1075+
1076+
FunctionDecl *FD = Template->getTemplatedDecl();
1077+
// Collect the list of template arguments relative to the 'primary'
1078+
// template. We need the entire list, since the constraint is completely
1079+
// uninstantiated at this point.
1080+
1081+
// FIXME: Add TemplateArgs through the 'Innermost' parameter once
1082+
// the refactoring of getTemplateInstantiationArgs() relands.
1083+
MultiLevelTemplateArgumentList MLTAL;
1084+
MLTAL.addOuterTemplateArguments(Template, std::nullopt, /*Final=*/false);
1085+
SemaRef.getTemplateInstantiationArgs(
1086+
MLTAL, /*D=*/FD, FD,
1087+
/*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true,
1088+
/*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true);
1089+
MLTAL.replaceInnermostTemplateArguments(Template, TemplateArgs);
1090+
1091+
Sema::ContextRAII SavedContext(SemaRef, FD);
1092+
std::optional<Sema::CXXThisScopeRAII> ThisScope;
1093+
if (auto *Method = dyn_cast<CXXMethodDecl>(FD))
1094+
ThisScope.emplace(SemaRef, /*Record=*/Method->getParent(),
1095+
/*ThisQuals=*/Method->getMethodQualifiers());
1096+
return SemaRef.CheckConstraintSatisfaction(
1097+
Template, TemplateAC, MLTAL, PointOfInstantiation, Satisfaction);
1098+
}
1099+
1100+
bool Sema::CheckFunctionTemplateConstraints(
10641101
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
10651102
ArrayRef<TemplateArgument> TemplateArgs,
10661103
ConstraintSatisfaction &Satisfaction) {
10671104
// In most cases we're not going to have constraints, so check for that first.
10681105
FunctionTemplateDecl *Template = Decl->getPrimaryTemplate();
1106+
1107+
if (!Template)
1108+
return ::CheckFunctionConstraintsWithoutInstantiation(
1109+
*this, PointOfInstantiation, Decl->getDescribedFunctionTemplate(),
1110+
TemplateArgs, Satisfaction);
1111+
10691112
// Note - code synthesis context for the constraints check is created
10701113
// inside CheckConstraintsSatisfaction.
10711114
SmallVector<AssociatedConstraint, 3> TemplateAC;

clang/lib/Sema/SemaOverload.cpp

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7848,11 +7848,14 @@ static void AddMethodTemplateCandidateImmediately(
78487848
/*PartialOrdering=*/false, ObjectType, ObjectClassification,
78497849
CandidateSet.getKind() ==
78507850
clang::OverloadCandidateSet::CSK_AddressOfOverloadSet,
7851-
[&](ArrayRef<QualType> ParamTypes) {
7851+
[&](ArrayRef<QualType> ParamTypes,
7852+
bool OnlyInitializeNonUserDefinedConversions) {
78527853
return S.CheckNonDependentConversions(
78537854
MethodTmpl, ParamTypes, Args, CandidateSet, Conversions,
7854-
SuppressUserConversions, ActingContext, ObjectType,
7855-
ObjectClassification, PO);
7855+
Sema::CheckNonDependentConversionsFlag(
7856+
SuppressUserConversions,
7857+
OnlyInitializeNonUserDefinedConversions),
7858+
ActingContext, ObjectType, ObjectClassification, PO);
78567859
});
78577860
Result != TemplateDeductionResult::Success) {
78587861
OverloadCandidate &Candidate =
@@ -7964,10 +7967,14 @@ static void AddTemplateOverloadCandidateImmediately(
79647967
/*ObjectClassification=*/Expr::Classification(),
79657968
CandidateSet.getKind() ==
79667969
OverloadCandidateSet::CSK_AddressOfOverloadSet,
7967-
[&](ArrayRef<QualType> ParamTypes) {
7970+
[&](ArrayRef<QualType> ParamTypes,
7971+
bool OnlyInitializeNonUserDefinedConversions) {
79687972
return S.CheckNonDependentConversions(
79697973
FunctionTemplate, ParamTypes, Args, CandidateSet, Conversions,
7970-
SuppressUserConversions, nullptr, QualType(), {}, PO);
7974+
Sema::CheckNonDependentConversionsFlag(
7975+
SuppressUserConversions,
7976+
OnlyInitializeNonUserDefinedConversions),
7977+
nullptr, QualType(), {}, PO);
79717978
});
79727979
Result != TemplateDeductionResult::Success) {
79737980
OverloadCandidate &Candidate =
@@ -8041,7 +8048,8 @@ void Sema::AddTemplateOverloadCandidate(
80418048
bool Sema::CheckNonDependentConversions(
80428049
FunctionTemplateDecl *FunctionTemplate, ArrayRef<QualType> ParamTypes,
80438050
ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,
8044-
ConversionSequenceList &Conversions, bool SuppressUserConversions,
8051+
ConversionSequenceList &Conversions,
8052+
CheckNonDependentConversionsFlag UserConversionFlag,
80458053
CXXRecordDecl *ActingContext, QualType ObjectType,
80468054
Expr::Classification ObjectClassification, OverloadCandidateParamOrder PO) {
80478055
// FIXME: The cases in which we allow explicit conversions for constructor
@@ -8054,8 +8062,9 @@ bool Sema::CheckNonDependentConversions(
80548062
bool HasThisConversion = Method && !isa<CXXConstructorDecl>(Method);
80558063
unsigned ThisConversions = HasThisConversion ? 1 : 0;
80568064

8057-
Conversions =
8058-
CandidateSet.allocateConversionSequences(ThisConversions + Args.size());
8065+
if (Conversions.empty())
8066+
Conversions =
8067+
CandidateSet.allocateConversionSequences(ThisConversions + Args.size());
80598068

80608069
// Overload resolution is always an unevaluated context.
80618070
EnterExpressionEvaluationContext Unevaluated(
@@ -8079,6 +8088,46 @@ bool Sema::CheckNonDependentConversions(
80798088
}
80808089
}
80818090

8091+
// A speculative workaround for self-dependent constraint bugs that manifest
8092+
// after CWG2369.
8093+
// FIXME: Add references to the standard once P3606 is adopted.
8094+
auto MaybeInvolveUserDefinedConversion = [&](QualType ParamType,
8095+
QualType ArgType) {
8096+
ParamType = ParamType.getNonReferenceType();
8097+
ArgType = ArgType.getNonReferenceType();
8098+
bool PointerConv = ParamType->isPointerType() && ArgType->isPointerType();
8099+
if (PointerConv) {
8100+
ParamType = ParamType->getPointeeType();
8101+
ArgType = ArgType->getPointeeType();
8102+
}
8103+
8104+
if (auto *RT = ParamType->getAs<RecordType>())
8105+
if (auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
8106+
RD && RD->hasDefinition()) {
8107+
if (llvm::any_of(LookupConstructors(RD), [](NamedDecl *ND) {
8108+
auto Info = getConstructorInfo(ND);
8109+
if (!Info)
8110+
return false;
8111+
CXXConstructorDecl *Ctor = Info.Constructor;
8112+
/// isConvertingConstructor takes copy/move constructors into
8113+
/// account!
8114+
return !Ctor->isCopyOrMoveConstructor() &&
8115+
Ctor->isConvertingConstructor(
8116+
/*AllowExplicit=*/true);
8117+
}))
8118+
return true;
8119+
}
8120+
8121+
if (auto *RT = ArgType->getAs<RecordType>())
8122+
if (auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
8123+
RD && RD->hasDefinition() &&
8124+
!RD->getVisibleConversionFunctions().empty()) {
8125+
return true;
8126+
}
8127+
8128+
return false;
8129+
};
8130+
80828131
unsigned Offset =
80838132
Method && Method->hasCXXExplicitFunctionObjectParameter() ? 1 : 0;
80848133

@@ -8099,13 +8148,16 @@ bool Sema::CheckNonDependentConversions(
80998148
// For members, 'this' got ConvIdx = 0 previously.
81008149
ConvIdx = ThisConversions + I;
81018150
}
8102-
Conversions[ConvIdx]
8103-
= TryCopyInitialization(*this, Args[I], ParamType,
8104-
SuppressUserConversions,
8105-
/*InOverloadResolution=*/true,
8106-
/*AllowObjCWritebackConversion=*/
8107-
getLangOpts().ObjCAutoRefCount,
8108-
AllowExplicit);
8151+
if (Conversions[ConvIdx].isInitialized())
8152+
continue;
8153+
if (UserConversionFlag.OnlyInitializeNonUserDefinedConversions &&
8154+
MaybeInvolveUserDefinedConversion(ParamType, Args[I]->getType()))
8155+
continue;
8156+
Conversions[ConvIdx] = TryCopyInitialization(
8157+
*this, Args[I], ParamType, UserConversionFlag.SuppressUserConversions,
8158+
/*InOverloadResolution=*/true,
8159+
/*AllowObjCWritebackConversion=*/
8160+
getLangOpts().ObjCAutoRefCount, AllowExplicit);
81098161
if (Conversions[ConvIdx].isBad())
81108162
return true;
81118163
}

0 commit comments

Comments
 (0)