Skip to content

Commit

Permalink
[clang] Instantiate concepts with sugared template arguments
Browse files Browse the repository at this point in the history
Since we don't unique specializations for concepts, we can just instantiate
them with the sugared template arguments, at negligible cost.

If we don't track their specializations, we can't resugar them later
anyway, and that would be more expensive than just instantiating them
sugared in the first place since it would require an additional pass.

Signed-off-by: Matheus Izvekov <mizvekov@gmail.com>

Differential Revision: https://reviews.llvm.org/D136566
  • Loading branch information
mizvekov committed Oct 26, 2022
1 parent 4c44c91 commit d0a6de5
Show file tree
Hide file tree
Showing 15 changed files with 160 additions and 144 deletions.
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaConcept.cpp
Expand Up @@ -1077,7 +1077,7 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
TemplateArgumentList TAL{TemplateArgumentList::OnStack,
CSE->getTemplateArguments()};
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
CSE->getNamedConcept(), /*Final=*/false, &TAL,
CSE->getNamedConcept(), /*Final=*/true, &TAL,
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConstraintInstantiation=*/true);
Expand Down
204 changes: 105 additions & 99 deletions clang/lib/Sema/SemaExprCXX.cpp
Expand Up @@ -1475,53 +1475,57 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
// C++2b:
// Otherwise, if the type contains a placeholder type, it is replaced by the
// type determined by placeholder type deduction.
DeducedType *Deduced = Ty->getContainedDeducedType();
if (Deduced && isa<DeducedTemplateSpecializationType>(Deduced)) {
Ty = DeduceTemplateSpecializationFromInitializer(TInfo, Entity,
Kind, Exprs);
if (Ty.isNull())
return ExprError();
Entity = InitializedEntity::InitializeTemporary(TInfo, Ty);
} else if (Deduced) {
MultiExprArg Inits = Exprs;
if (ListInitialization) {
auto *ILE = cast<InitListExpr>(Exprs[0]);
Inits = MultiExprArg(ILE->getInits(), ILE->getNumInits());
}
if (const DeducedType *Deduced = Ty->getContainedDeducedType();
Deduced && !Deduced->isDeduced()) {
if (isa<DeducedTemplateSpecializationType>(Deduced)) {
Ty = DeduceTemplateSpecializationFromInitializer(TInfo, Entity, Kind,
Exprs);
if (Ty.isNull())
return ExprError();
Entity = InitializedEntity::InitializeTemporary(TInfo, Ty);
} else {
assert(isa<AutoType>(Deduced));
MultiExprArg Inits = Exprs;
if (ListInitialization) {
auto *ILE = cast<InitListExpr>(Exprs[0]);
Inits = MultiExprArg(ILE->getInits(), ILE->getNumInits());
}

if (Inits.empty())
return ExprError(Diag(TyBeginLoc, diag::err_auto_expr_init_no_expression)
<< Ty << FullRange);
if (Inits.size() > 1) {
Expr *FirstBad = Inits[1];
return ExprError(Diag(FirstBad->getBeginLoc(),
diag::err_auto_expr_init_multiple_expressions)
<< Ty << FullRange);
}
if (getLangOpts().CPlusPlus2b) {
if (Ty->getAs<AutoType>())
Diag(TyBeginLoc, diag::warn_cxx20_compat_auto_expr) << FullRange;
}
Expr *Deduce = Inits[0];
if (isa<InitListExpr>(Deduce))
return ExprError(
Diag(Deduce->getBeginLoc(), diag::err_auto_expr_init_paren_braces)
<< ListInitialization << Ty << FullRange);
QualType DeducedType;
TemplateDeductionInfo Info(Deduce->getExprLoc());
TemplateDeductionResult Result =
DeduceAutoType(TInfo->getTypeLoc(), Deduce, DeducedType, Info);
if (Result != TDK_Success && Result != TDK_AlreadyDiagnosed)
return ExprError(Diag(TyBeginLoc, diag::err_auto_expr_deduction_failure)
<< Ty << Deduce->getType() << FullRange
<< Deduce->getSourceRange());
if (DeducedType.isNull()) {
assert(Result == TDK_AlreadyDiagnosed);
return ExprError();
}
if (Inits.empty())
return ExprError(
Diag(TyBeginLoc, diag::err_auto_expr_init_no_expression)
<< Ty << FullRange);
if (Inits.size() > 1) {
Expr *FirstBad = Inits[1];
return ExprError(Diag(FirstBad->getBeginLoc(),
diag::err_auto_expr_init_multiple_expressions)
<< Ty << FullRange);
}
if (getLangOpts().CPlusPlus2b) {
if (Ty->getAs<AutoType>())
Diag(TyBeginLoc, diag::warn_cxx20_compat_auto_expr) << FullRange;
}
Expr *Deduce = Inits[0];
if (isa<InitListExpr>(Deduce))
return ExprError(
Diag(Deduce->getBeginLoc(), diag::err_auto_expr_init_paren_braces)
<< ListInitialization << Ty << FullRange);
QualType DeducedType;
TemplateDeductionInfo Info(Deduce->getExprLoc());
TemplateDeductionResult Result =
DeduceAutoType(TInfo->getTypeLoc(), Deduce, DeducedType, Info);
if (Result != TDK_Success && Result != TDK_AlreadyDiagnosed)
return ExprError(Diag(TyBeginLoc, diag::err_auto_expr_deduction_failure)
<< Ty << Deduce->getType() << FullRange
<< Deduce->getSourceRange());
if (DeducedType.isNull()) {
assert(Result == TDK_AlreadyDiagnosed);
return ExprError();
}

Ty = DeducedType;
Entity = InitializedEntity::InitializeTemporary(TInfo, Ty);
Ty = DeducedType;
Entity = InitializedEntity::InitializeTemporary(TInfo, Ty);
}
}

if (Ty->isDependentType() || CallExpr::hasAnyTypeDependentArguments(Exprs)) {
Expand Down Expand Up @@ -2011,59 +2015,62 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
DirectInitRange.getEnd());

// C++11 [dcl.spec.auto]p6. Deduce the type which 'auto' stands in for.
auto *Deduced = AllocType->getContainedDeducedType();
if (Deduced && isa<DeducedTemplateSpecializationType>(Deduced)) {
if (ArraySize)
return ExprError(
Diag(*ArraySize ? (*ArraySize)->getExprLoc() : TypeRange.getBegin(),
diag::err_deduced_class_template_compound_type)
<< /*array*/ 2
<< (*ArraySize ? (*ArraySize)->getSourceRange() : TypeRange));
if (const DeducedType *Deduced = AllocType->getContainedDeducedType();
Deduced && !Deduced->isDeduced()) {
if (isa<DeducedTemplateSpecializationType>(Deduced)) {
if (ArraySize)
return ExprError(
Diag(*ArraySize ? (*ArraySize)->getExprLoc() : TypeRange.getBegin(),
diag::err_deduced_class_template_compound_type)
<< /*array*/ 2
<< (*ArraySize ? (*ArraySize)->getSourceRange() : TypeRange));

InitializedEntity Entity =
InitializedEntity::InitializeNew(StartLoc, AllocType);
AllocType = DeduceTemplateSpecializationFromInitializer(
AllocTypeInfo, Entity, Kind, Exprs);
if (AllocType.isNull())
return ExprError();
} else {
assert(isa<AutoType>(Deduced));
MultiExprArg Inits = Exprs;
bool Braced = (initStyle == CXXNewExpr::ListInit);
if (Braced) {
auto *ILE = cast<InitListExpr>(Exprs[0]);
Inits = MultiExprArg(ILE->getInits(), ILE->getNumInits());
}

InitializedEntity Entity
= InitializedEntity::InitializeNew(StartLoc, AllocType);
AllocType = DeduceTemplateSpecializationFromInitializer(
AllocTypeInfo, Entity, Kind, Exprs);
if (AllocType.isNull())
return ExprError();
} else if (Deduced) {
MultiExprArg Inits = Exprs;
bool Braced = (initStyle == CXXNewExpr::ListInit);
if (Braced) {
auto *ILE = cast<InitListExpr>(Exprs[0]);
Inits = MultiExprArg(ILE->getInits(), ILE->getNumInits());
}

if (initStyle == CXXNewExpr::NoInit || Inits.empty())
return ExprError(Diag(StartLoc, diag::err_auto_new_requires_ctor_arg)
<< AllocType << TypeRange);
if (Inits.size() > 1) {
Expr *FirstBad = Inits[1];
return ExprError(Diag(FirstBad->getBeginLoc(),
diag::err_auto_new_ctor_multiple_expressions)
<< AllocType << TypeRange);
}
if (Braced && !getLangOpts().CPlusPlus17)
Diag(Initializer->getBeginLoc(), diag::ext_auto_new_list_init)
<< AllocType << TypeRange;
Expr *Deduce = Inits[0];
if (isa<InitListExpr>(Deduce))
return ExprError(
Diag(Deduce->getBeginLoc(), diag::err_auto_expr_init_paren_braces)
<< Braced << AllocType << TypeRange);
QualType DeducedType;
TemplateDeductionInfo Info(Deduce->getExprLoc());
TemplateDeductionResult Result =
DeduceAutoType(AllocTypeInfo->getTypeLoc(), Deduce, DeducedType, Info);
if (Result != TDK_Success && Result != TDK_AlreadyDiagnosed)
return ExprError(Diag(StartLoc, diag::err_auto_new_deduction_failure)
<< AllocType << Deduce->getType() << TypeRange
<< Deduce->getSourceRange());
if (DeducedType.isNull()) {
assert(Result == TDK_AlreadyDiagnosed);
return ExprError();
if (initStyle == CXXNewExpr::NoInit || Inits.empty())
return ExprError(Diag(StartLoc, diag::err_auto_new_requires_ctor_arg)
<< AllocType << TypeRange);
if (Inits.size() > 1) {
Expr *FirstBad = Inits[1];
return ExprError(Diag(FirstBad->getBeginLoc(),
diag::err_auto_new_ctor_multiple_expressions)
<< AllocType << TypeRange);
}
if (Braced && !getLangOpts().CPlusPlus17)
Diag(Initializer->getBeginLoc(), diag::ext_auto_new_list_init)
<< AllocType << TypeRange;
Expr *Deduce = Inits[0];
if (isa<InitListExpr>(Deduce))
return ExprError(
Diag(Deduce->getBeginLoc(), diag::err_auto_expr_init_paren_braces)
<< Braced << AllocType << TypeRange);
QualType DeducedType;
TemplateDeductionInfo Info(Deduce->getExprLoc());
TemplateDeductionResult Result = DeduceAutoType(
AllocTypeInfo->getTypeLoc(), Deduce, DeducedType, Info);
if (Result != TDK_Success && Result != TDK_AlreadyDiagnosed)
return ExprError(Diag(StartLoc, diag::err_auto_new_deduction_failure)
<< AllocType << Deduce->getType() << TypeRange
<< Deduce->getSourceRange());
if (DeducedType.isNull()) {
assert(Result == TDK_AlreadyDiagnosed);
return ExprError();
}
AllocType = DeducedType;
}
AllocType = DeducedType;
}

// Per C++0x [expr.new]p5, the type being constructed may be a
Expand Down Expand Up @@ -8956,16 +8963,15 @@ Sema::BuildExprRequirement(
// be satisfied.
TemplateParameterList *TPL =
ReturnTypeRequirement.getTypeConstraintTemplateParameterList();
QualType MatchedType =
Context.getReferenceQualifiedType(E).getCanonicalType();
QualType MatchedType = Context.getReferenceQualifiedType(E);
llvm::SmallVector<TemplateArgument, 1> Args;
Args.push_back(TemplateArgument(MatchedType));

auto *Param = cast<TemplateTypeParmDecl>(TPL->getParam(0));

TemplateArgumentList TAL(TemplateArgumentList::OnStack, Args);
MultiLevelTemplateArgumentList MLTAL(Param, TAL.asArray(),
/*Final=*/false);
/*Final=*/true);
MLTAL.addOuterRetainedLevels(TPL->getDepth());
Expr *IDC = Param->getTypeConstraint()->getImmediatelyDeclaredConstraint();
ExprResult Constraint = SubstExpr(IDC, MLTAL);
Expand Down
12 changes: 6 additions & 6 deletions clang/lib/Sema/SemaTemplate.cpp
Expand Up @@ -4861,13 +4861,13 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,

auto *CSD = ImplicitConceptSpecializationDecl::Create(
Context, NamedConcept->getDeclContext(), NamedConcept->getLocation(),
CanonicalConverted);
SugaredConverted);
ConstraintSatisfaction Satisfaction;
bool AreArgsDependent =
TemplateSpecializationType::anyDependentTemplateArguments(
*TemplateArgs, CanonicalConverted);
MultiLevelTemplateArgumentList MLTAL(NamedConcept, CanonicalConverted,
/*Final=*/false);
*TemplateArgs, SugaredConverted);
MultiLevelTemplateArgumentList MLTAL(NamedConcept, SugaredConverted,
/*Final=*/true);
LocalInstantiationScope Scope(*this);

EnterExpressionEvaluationContext EECtx{
Expand Down Expand Up @@ -6098,7 +6098,7 @@ bool Sema::CheckTemplateArgumentList(

if (!PartialTemplateArgs) {
TemplateArgumentList StackTemplateArgs(TemplateArgumentList::OnStack,
CanonicalConverted);
SugaredConverted);
// Setup the context/ThisScope for the case where we are needing to
// re-instantiate constraints outside of normal instantiation.
DeclContext *NewContext = Template->getDeclContext();
Expand All @@ -6118,7 +6118,7 @@ bool Sema::CheckTemplateArgumentList(
CXXThisScopeRAII(*this, RD, ThisQuals, RD != nullptr);

MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs(
Template, /*Final=*/false, &StackTemplateArgs,
Template, /*Final=*/true, &StackTemplateArgs,
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConceptInstantiation=*/true);
Expand Down
10 changes: 5 additions & 5 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Expand Up @@ -2886,10 +2886,10 @@ CheckDeducedArgumentConstraints(Sema &S, TemplateDeclT *Template,

bool NeedsReplacement = DeducedArgsNeedReplacement(Template);
TemplateArgumentList DeducedTAL{TemplateArgumentList::OnStack,
CanonicalDeducedArgs};
SugaredDeducedArgs};

MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
Template, /*Final=*/false,
Template, /*Final=*/true,
/*InnerMost=*/NeedsReplacement ? nullptr : &DeducedTAL,
/*RelativeToPrimary=*/true, /*Pattern=*/
nullptr, /*ForConstraintInstantiation=*/true);
Expand All @@ -2899,7 +2899,7 @@ CheckDeducedArgumentConstraints(Sema &S, TemplateDeclT *Template,
// not class-scope explicit specialization, so replace with Deduced Args
// instead of adding to inner-most.
if (NeedsReplacement)
MLTAL.replaceInnermostTemplateArguments(CanonicalDeducedArgs);
MLTAL.replaceInnermostTemplateArguments(SugaredDeducedArgs);

if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL,
Info.getLocation(),
Expand Down Expand Up @@ -4672,8 +4672,8 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
/*PartialTemplateArgs=*/false,
SugaredConverted, CanonicalConverted))
return true;
MultiLevelTemplateArgumentList MLTAL(Concept, CanonicalConverted,
/*Final=*/false);
MultiLevelTemplateArgumentList MLTAL(Concept, SugaredConverted,
/*Final=*/true);
if (S.CheckConstraintSatisfaction(Concept, {Concept->getConstraintExpr()},
MLTAL, TypeLoc.getLocalSourceRange(),
Satisfaction))
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Serialization/ASTReaderDecl.cpp
Expand Up @@ -2243,7 +2243,7 @@ void ASTDeclReader::VisitImplicitConceptSpecializationDecl(
VisitDecl(D);
llvm::SmallVector<TemplateArgument, 4> Args;
for (unsigned I = 0; I < D->NumTemplateArgs; ++I)
Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/true));
Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/false));
D->setTemplateArguments(Args);
}

Expand Down
10 changes: 6 additions & 4 deletions clang/test/AST/ast-dump-concepts.cpp
Expand Up @@ -20,8 +20,9 @@ struct Foo {
// CHECK: TemplateTypeParmDecl {{.*}} referenced Concept {{.*}} 'binary_concept'
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} <col:13, col:31> 'bool' Concept {{.*}} 'binary_concept'
// CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} <line:13:9> col:9
// CHECK-NEXT: | |-TemplateArgument type 'type-parameter-1-0'
// CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent {{.*}}depth 1 index 0
// CHECK-NEXT: | |-TemplateArgument type 'R'
// CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'R' dependent {{.*}}depth 1 index 0
// CHECK-NEXT: | | `-TemplateTypeParm {{.*}} 'R'
// CHECK-NEXT: | `-TemplateArgument type 'int'
// CHECK-NEXT: | `-BuiltinType {{.*}} 'int'
// CHECK-NEXT: |-TemplateArgument {{.*}} type 'R'
Expand All @@ -35,8 +36,9 @@ struct Foo {
// CHECK: TemplateTypeParmDecl {{.*}} referenced Concept {{.*}} 'unary_concept'
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} <col:13> 'bool'
// CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} <line:10:9> col:9
// CHECK-NEXT: | `-TemplateArgument type 'type-parameter-1-0'
// CHECK-NEXT: | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent {{.*}}depth 1 index 0
// CHECK-NEXT: | `-TemplateArgument type 'R'
// CHECK-NEXT: | `-TemplateTypeParmType {{.*}} 'R' dependent {{.*}}depth 1 index 0
// CHECK-NEXT: | `-TemplateTypeParm {{.*}} 'R'
template <unary_concept R>
Foo(R);

Expand Down
Expand Up @@ -35,14 +35,14 @@ using r2i2 = r2<A>; // expected-error{{constraints not satisfied for class templ
using r2i3 = r2<D>;
using r2i4 = r2<const D>; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}}

template<typename T> requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}}
template<typename T> requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'class nonexistent'}}
struct r3 {};

using r3i1 = r3<int>;
using r3i2 = r3<A>;
using r3i3 = r3<A &>;
using r3i4 = r3<void>; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}}
using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}}
using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = class nonexistent]}}

// Non-dependent expressions

Expand Down Expand Up @@ -149,7 +149,7 @@ namespace std_example {
template<typename T> constexpr bool is_same_v<T, T> = true;

template<typename T, typename U> concept same_as = is_same_v<T, U>;
// expected-note@-1 {{because 'is_same_v<int, int *>' evaluated to false}}
// expected-note@-1 {{because 'is_same_v<int, typename T2::inner>' evaluated to false}}

static_assert(C1<int>);
static_assert(C1<int*>);
Expand All @@ -173,9 +173,9 @@ namespace std_example {
int operator *() { return 0; }
};
static_assert(C2<T1>);
template<C2 T> struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'std_example::T2' does not satisfy 'C2'}}
template<C2 T> struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'T2' does not satisfy 'C2'}}
using c2c1 = C2_check<int>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = int]}}
using c2c2 = C2_check<T2>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::T2]}}
using c2c2 = C2_check<T2>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = T2]}}

template<typename T>
void g(T t) noexcept(sizeof(T) == 1) {}
Expand Down
Expand Up @@ -27,7 +27,7 @@ using r4i = X<void>::r4<int>; // expected-error{{constraints not satisfied for c

// C++ [expr.prim.req.nested] Examples
namespace std_example {
template<typename U> concept C1 = sizeof(U) == 1; // expected-note{{because 'sizeof(int) == 1' (4 == 1) evaluated to false}}
template<typename U> concept C1 = sizeof(U) == 1; // expected-note{{because 'sizeof(decltype(+t)) == 1' (4 == 1) evaluated to false}}
template<typename T> concept D =
requires (T t) {
requires C1<decltype (+t)>; // expected-note{{because 'decltype(+t)' (aka 'int') does not satisfy 'C1'}}
Expand Down

0 comments on commit d0a6de5

Please sign in to comment.