[clang] make concept normalization a SFINAE context (#197356)
It is incorrect to allow the substitution failures in concept normalization to make the program ill-formed. These can happen when comparing the equivalence of constraints for redeclaration checking, and a failure here only means these declarations are not equivalent. For now, these diagnostics are simply discarded. It would be nice if some day, as part of diagnostics for non-matching out-of-line definitions, we would explain why matching failed for each candidate, and then add these as notes explaining why the constraints were not equivalent.
This commit is contained in:
parent
c501f3747a
commit
bb38a428e0
8 changed files with 47 additions and 96 deletions
|
|
@ -180,7 +180,6 @@ const char *getMainFileRange(const Diag &D, const SourceManager &SM,
|
|||
case diag::note_checking_constraints_for_class_spec_id_here:
|
||||
case diag::note_checking_constraints_for_function_here:
|
||||
case diag::note_constraint_substitution_here:
|
||||
case diag::note_constraint_normalization_here:
|
||||
case diag::note_parameter_mapping_substitution_here:
|
||||
R = N.Range;
|
||||
return "in template";
|
||||
|
|
|
|||
|
|
@ -593,6 +593,8 @@ Bug Fixes to C++ Support
|
|||
- Fixed a crash when evaluating uninitialized GCC vector/ext_vector_type vectors in ``constexpr``. (#GH180044)
|
||||
- Fixed a crash when `explicit(bool)` is used with an incomplete enumeration. (#GH183887)
|
||||
- Fixed a crash on ``typeid`` of incomplete local types during template instantiation. (#GH63242), (#GH176397)
|
||||
- Fixed spurious diagnostics produced when checking if constraints are equivalent for redeclarations,
|
||||
which could make the program mistakenly ill-formed.
|
||||
- Fixed a crash when an immediate-invoked ``consteval`` lambda is used as an invalid initializer. (#GH185270)
|
||||
- Fixed an assertion failure when using a global destructor with a target with a non-default program address space. (#GH186484)
|
||||
|
||||
|
|
|
|||
|
|
@ -6027,8 +6027,6 @@ 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<
|
||||
"while calculating associated constraint of template %0 here">;
|
||||
def note_parameter_mapping_substitution_here : Note<
|
||||
"while substituting into concept arguments here; substitution failures not "
|
||||
"allowed in concept arguments">;
|
||||
|
|
|
|||
|
|
@ -13273,9 +13273,6 @@ public:
|
|||
// We are substituting template arguments into a constraint expression.
|
||||
ConstraintSubstitution,
|
||||
|
||||
// We are normalizing a constraint expression.
|
||||
ConstraintNormalization,
|
||||
|
||||
// Instantiating a Requires Expression parameter clause.
|
||||
RequirementParameterInstantiation,
|
||||
|
||||
|
|
@ -13494,12 +13491,6 @@ public:
|
|||
ConstraintSubstitution, NamedDecl *Template,
|
||||
SourceRange InstantiationRange);
|
||||
|
||||
struct ConstraintNormalization {};
|
||||
/// \brief Note that we are normalizing a constraint expression.
|
||||
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
|
||||
ConstraintNormalization, NamedDecl *Template,
|
||||
SourceRange InstantiationRange);
|
||||
|
||||
struct ParameterMappingSubstitution {};
|
||||
/// \brief Note that we are subtituting into the parameter mapping of an
|
||||
/// atomic constraint during constraint normalization.
|
||||
|
|
|
|||
|
|
@ -450,8 +450,6 @@ private:
|
|||
return "ConstraintsCheck";
|
||||
case CodeSynthesisContext::ConstraintSubstitution:
|
||||
return "ConstraintSubstitution";
|
||||
case CodeSynthesisContext::ConstraintNormalization:
|
||||
return "ConstraintNormalization";
|
||||
case CodeSynthesisContext::RequirementParameterInstantiation:
|
||||
return "RequirementParameterInstantiation";
|
||||
case CodeSynthesisContext::ParameterMappingSubstitution:
|
||||
|
|
|
|||
|
|
@ -1316,7 +1316,7 @@ static ExprResult
|
|||
SubstituteConceptsInConstraintExpression(Sema &S, const NamedDecl *D,
|
||||
const ConceptSpecializationExpr *CSE,
|
||||
UnsignedOrNone SubstIndex) {
|
||||
|
||||
Sema::SFINAETrap Trap(S);
|
||||
// [C++2c] [temp.constr.normal]
|
||||
// Otherwise, to form CE, any non-dependent concept template argument Ai
|
||||
// is substituted into the constraint-expression of C.
|
||||
|
|
@ -1515,14 +1515,6 @@ static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
|
|||
if (MLTAL.getNumSubstitutedLevels() == 0)
|
||||
return ConstrExpr;
|
||||
|
||||
Sema::NonSFINAEContext _(S);
|
||||
Sema::InstantiatingTemplate Inst(
|
||||
S, DeclInfo.getLocation(),
|
||||
Sema::InstantiatingTemplate::ConstraintNormalization{},
|
||||
const_cast<NamedDecl *>(DeclInfo.getDecl()), SourceRange{});
|
||||
if (Inst.isInvalid())
|
||||
return nullptr;
|
||||
|
||||
// Set up a dummy 'instantiation' scope in the case of reference to function
|
||||
// parameters that the surrounding function hasn't been instantiated yet. Note
|
||||
// this may happen while we're comparing two templates' constraint
|
||||
|
|
@ -1604,6 +1596,7 @@ bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
|
|||
// C++ [temp.constr.decl]p4
|
||||
if (Old && !New.isInvalid() && !New.ContainsDecl(Old) &&
|
||||
Old->getLexicalDeclContext() != New.getLexicalDeclContext()) {
|
||||
Sema::SFINAETrap _(*this);
|
||||
if (const Expr *SubstConstr =
|
||||
SubstituteConstraintExpressionWithoutSatisfaction(*this, Old,
|
||||
OldConstr))
|
||||
|
|
@ -2406,51 +2399,36 @@ NormalizedConstraint *NormalizedConstraint::fromConstraintExpr(
|
|||
|
||||
return CompoundConstraint::Create(
|
||||
S.Context, LHS, BO.isAnd() ? CCK_Conjunction : CCK_Disjunction, RHS);
|
||||
} else if (auto *CSE = dyn_cast<const ConceptSpecializationExpr>(E)) {
|
||||
}
|
||||
if (auto *CSE = dyn_cast<const ConceptSpecializationExpr>(E)) {
|
||||
// C++ [temp.constr.normal]p1.1
|
||||
// [...]
|
||||
// The normal form of an id-expression of the form C<A1, A2, ..., AN>,
|
||||
// where C names a concept, is the normal form of the
|
||||
// constraint-expression of C, after substituting A1, A2, ..., AN for C’s
|
||||
// respective template parameters in the parameter mappings in each atomic
|
||||
// constraint. If any such substitution results in an invalid type or
|
||||
// expression, the program is ill-formed; no diagnostic is required.
|
||||
// [...]
|
||||
NormalizedConstraint *SubNF;
|
||||
{
|
||||
Sema::NonSFINAEContext _(S);
|
||||
Sema::InstantiatingTemplate Inst(
|
||||
S, CSE->getExprLoc(),
|
||||
Sema::InstantiatingTemplate::ConstraintNormalization{},
|
||||
// FIXME: improve const-correctness of InstantiatingTemplate
|
||||
const_cast<NamedDecl *>(D), CSE->getSourceRange());
|
||||
if (Inst.isInvalid())
|
||||
return nullptr;
|
||||
// C++ [temp.constr.normal]p1.1
|
||||
// [...]
|
||||
// The normal form of an id-expression of the form C<A1, A2, ..., AN>,
|
||||
// where C names a concept, is the normal form of the
|
||||
// constraint-expression of C, after substituting A1, A2, ..., AN for C’s
|
||||
// respective template parameters in the parameter mappings in each atomic
|
||||
// constraint. If any such substitution results in an invalid type or
|
||||
// expression, the program is ill-formed; no diagnostic is required.
|
||||
// [...]
|
||||
|
||||
// Use canonical declarations to merge ConceptDecls across
|
||||
// different modules.
|
||||
ConceptDecl *CD = CSE->getNamedConcept()->getCanonicalDecl();
|
||||
|
||||
ExprResult Res =
|
||||
SubstituteConceptsInConstraintExpression(S, D, CSE, SubstIndex);
|
||||
if (!Res.isUsable())
|
||||
return nullptr;
|
||||
|
||||
if (ExprResult Res =
|
||||
SubstituteConceptsInConstraintExpression(S, D, CSE, SubstIndex);
|
||||
Res.isUsable())
|
||||
// Use canonical declarations to merge ConceptDecls across different
|
||||
// modules.
|
||||
SubNF = NormalizedConstraint::fromAssociatedConstraints(
|
||||
S, CD, AssociatedConstraint(Res.get(), SubstIndex));
|
||||
|
||||
if (!SubNF)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
S, CSE->getNamedConcept()->getCanonicalDecl(),
|
||||
AssociatedConstraint(Res.get(), SubstIndex));
|
||||
else
|
||||
return nullptr;
|
||||
return ConceptIdConstraint::Create(S.getASTContext(),
|
||||
CSE->getConceptReference(), SubNF, D,
|
||||
CSE, SubstIndex);
|
||||
|
||||
} else if (auto *FE = dyn_cast<const CXXFoldExpr>(E);
|
||||
FE && S.getLangOpts().CPlusPlus26 &&
|
||||
(FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
|
||||
FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
|
||||
}
|
||||
if (auto *FE = dyn_cast<const CXXFoldExpr>(E);
|
||||
FE && S.getLangOpts().CPlusPlus26 &&
|
||||
(FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
|
||||
FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
|
||||
|
||||
// Normalize fold expressions in C++26.
|
||||
|
||||
|
|
|
|||
|
|
@ -583,7 +583,6 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
|
|||
case ExceptionSpecEvaluation:
|
||||
case ConstraintSubstitution:
|
||||
case ParameterMappingSubstitution:
|
||||
case ConstraintNormalization:
|
||||
case RewritingOperatorAsSpaceship:
|
||||
case InitializingStructuredBinding:
|
||||
case MarkingClassDllexported:
|
||||
|
|
@ -793,14 +792,6 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
|
|||
SemaRef, CodeSynthesisContext::ConstraintSubstitution,
|
||||
PointOfInstantiation, InstantiationRange, Template, nullptr, {}) {}
|
||||
|
||||
Sema::InstantiatingTemplate::InstantiatingTemplate(
|
||||
Sema &SemaRef, SourceLocation PointOfInstantiation,
|
||||
ConstraintNormalization, NamedDecl *Template,
|
||||
SourceRange InstantiationRange)
|
||||
: InstantiatingTemplate(
|
||||
SemaRef, CodeSynthesisContext::ConstraintNormalization,
|
||||
PointOfInstantiation, InstantiationRange, Template) {}
|
||||
|
||||
Sema::InstantiatingTemplate::InstantiatingTemplate(
|
||||
Sema &SemaRef, SourceLocation PointOfInstantiation,
|
||||
ParameterMappingSubstitution, NamedDecl *Template,
|
||||
|
|
@ -1245,12 +1236,6 @@ void Sema::PrintInstantiationStack(InstantiationContextDiagFuncRef DiagFunc) {
|
|||
PDiag(diag::note_constraint_substitution_here)
|
||||
<< Active->InstantiationRange);
|
||||
break;
|
||||
case CodeSynthesisContext::ConstraintNormalization:
|
||||
DiagFunc(Active->PointOfInstantiation,
|
||||
PDiag(diag::note_constraint_normalization_here)
|
||||
<< cast<NamedDecl>(Active->Entity)
|
||||
<< Active->InstantiationRange);
|
||||
break;
|
||||
case CodeSynthesisContext::ParameterMappingSubstitution:
|
||||
DiagFunc(Active->PointOfInstantiation,
|
||||
PDiag(diag::note_parameter_mapping_substitution_here)
|
||||
|
|
@ -2862,7 +2847,7 @@ TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T,
|
|||
SourceLocation Loc,
|
||||
DeclarationName Entity,
|
||||
bool AllowDeducedTST) {
|
||||
assert(!CodeSynthesisContexts.empty() &&
|
||||
assert((!CodeSynthesisContexts.empty() || isSFINAEContext()) &&
|
||||
"Cannot perform an instantiation without some context on the "
|
||||
"instantiation stack");
|
||||
|
||||
|
|
@ -2879,7 +2864,7 @@ TypeSourceInfo *Sema::SubstType(TypeLoc TL,
|
|||
const MultiLevelTemplateArgumentList &Args,
|
||||
SourceLocation Loc,
|
||||
DeclarationName Entity) {
|
||||
assert(!CodeSynthesisContexts.empty() &&
|
||||
assert((!CodeSynthesisContexts.empty() || isSFINAEContext()) &&
|
||||
"Cannot perform an instantiation without some context on the "
|
||||
"instantiation stack");
|
||||
|
||||
|
|
@ -2910,7 +2895,7 @@ QualType Sema::SubstType(QualType T,
|
|||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
SourceLocation Loc, DeclarationName Entity,
|
||||
bool *IsIncompleteSubstitution) {
|
||||
assert(!CodeSynthesisContexts.empty() &&
|
||||
assert((!CodeSynthesisContexts.empty() || isSFINAEContext()) &&
|
||||
"Cannot perform an instantiation without some context on the "
|
||||
"instantiation stack");
|
||||
|
||||
|
|
@ -2957,7 +2942,7 @@ TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T,
|
|||
CXXRecordDecl *ThisContext,
|
||||
Qualifiers ThisTypeQuals,
|
||||
bool EvaluateConstraints) {
|
||||
assert(!CodeSynthesisContexts.empty() &&
|
||||
assert((!CodeSynthesisContexts.empty() || isSFINAEContext()) &&
|
||||
"Cannot perform an instantiation without some context on the "
|
||||
"instantiation stack");
|
||||
|
||||
|
|
@ -3274,7 +3259,7 @@ bool Sema::SubstParmTypes(
|
|||
SmallVectorImpl<QualType> &ParamTypes,
|
||||
SmallVectorImpl<ParmVarDecl *> *OutParams,
|
||||
ExtParameterInfoBuilder &ParamInfos) {
|
||||
assert(!CodeSynthesisContexts.empty() &&
|
||||
assert((!CodeSynthesisContexts.empty() || isSFINAEContext()) &&
|
||||
"Cannot perform an instantiation without some context on the "
|
||||
"instantiation stack");
|
||||
|
||||
|
|
@ -4502,6 +4487,8 @@ ExprResult Sema::SubstConstraintExprWithoutSatisfaction(
|
|||
ExprResult Sema::SubstConceptTemplateArguments(
|
||||
const ConceptSpecializationExpr *CSE, const Expr *ConstraintExpr,
|
||||
const MultiLevelTemplateArgumentList &MLTAL) {
|
||||
assert(isSFINAEContext());
|
||||
|
||||
TemplateInstantiator Instantiator(*this, MLTAL, SourceLocation(),
|
||||
DeclarationName());
|
||||
const ASTTemplateArgumentListInfo *ArgsAsWritten =
|
||||
|
|
@ -4509,16 +4496,6 @@ ExprResult Sema::SubstConceptTemplateArguments(
|
|||
TemplateArgumentListInfo SubstArgs(ArgsAsWritten->getLAngleLoc(),
|
||||
ArgsAsWritten->getRAngleLoc());
|
||||
|
||||
NonSFINAEContext _(*this);
|
||||
Sema::InstantiatingTemplate Inst(
|
||||
*this, ArgsAsWritten->arguments().front().getSourceRange().getBegin(),
|
||||
Sema::InstantiatingTemplate::ConstraintNormalization{},
|
||||
CSE->getNamedConcept(),
|
||||
ArgsAsWritten->arguments().front().getSourceRange());
|
||||
|
||||
if (Inst.isInvalid())
|
||||
return ExprError();
|
||||
|
||||
if (Instantiator.TransformConceptTemplateArguments(
|
||||
ArgsAsWritten->getTemplateArgs(),
|
||||
ArgsAsWritten->getTemplateArgs() +
|
||||
|
|
|
|||
|
|
@ -228,9 +228,7 @@ namespace constrained_member_sfinae {
|
|||
|
||||
template<int N> struct S {
|
||||
template<class T>
|
||||
static constexpr int constrained_method() requires (sizeof(int[N * 1073741824 + 4]) == 16) { // expected-warning {{variable length arrays in C++ are a Clang extension}} \
|
||||
expected-note {{value 4294967296 is outside the range of representable values of type 'int'}} \
|
||||
expected-note {{while calculating associated constraint of template 'constrained_method' here}}
|
||||
static constexpr int constrained_method() requires (sizeof(int[N * 1073741824 + 4]) == 16) {
|
||||
return CONSTRAINED_METHOD_1;
|
||||
}
|
||||
|
||||
|
|
@ -897,3 +895,13 @@ template<typename Q> requires is_valid<Q>
|
|||
class Inner2 {};
|
||||
|
||||
}
|
||||
|
||||
namespace NoDiagnosticsInNormalization {
|
||||
template<class T> struct S {
|
||||
template<class> void f() requires (T::v > 0) {}
|
||||
template<class> void f() requires (sizeof(T) > 1);
|
||||
};
|
||||
template<>
|
||||
template<class>
|
||||
void S<int>::f() requires (sizeof(int) > 1) {}
|
||||
} // namespace NoDiagnosticsInNormalization
|
||||
|
|
|
|||
Loading…
Reference in a new issue