Skip to content

Commit

Permalink
[Clang] Fix dependence handling of nttp for variable templates (#69075)
Browse files Browse the repository at this point in the history
The dependence of a template argument is not only determined by the
argument itself, but also by the type of the template parameter:

> Furthermore, a non-type
[template-argument](https://eel.is/c++draft/temp.names#nt:template-argument)
is dependent if the corresponding non-type
[template-parameter](https://eel.is/c++draft/temp.param#nt:template-parameter)
is of reference or pointer type and the
[template-argument](https://eel.is/c++draft/temp.names#nt:template-argument)
designates or points to a member of the current instantiation or a
member of a dependent
type[.](https://eel.is/c++draft/temp.dep#temp-3.sentence-1)

For example:

```cpp
struct A{};

template <const A& T>
const A JoinStringViews = T;

template <int V>
class Builder {
public:
    static constexpr A Equal{};
    static constexpr auto Val = JoinStringViews<Equal>;
};
```

The constant expression `Equal` is not dependent, but because the type
of the template parameter is a reference type and `Equal` is a member of
the current instantiation, the template argument of
`JoinStringViews<Equal>` is actually dependent, which makes
`JoinStringViews<Equal>` dependent.

When a template-id of a variable template is dependent,
`CheckVarTemplateId` will return an `UnresolvedLookupExpr`, but
`UnresolvedLookupExpr` calculates dependence by template arguments only
(the `ConstantExpr` `Equal` here), which is not dependent. This causes
type deduction to think that `JoinStringViews<Equal>` is `OverloadTy`
and treat it as a function template, which is clearly wrong.

This PR adds a `KnownDependent` parameter to the constructor of
`UnresolvedLookupExpr`. After canonicalization, if `CanonicalConverted`
contains any dependent argument, `KnownDependent` is set to `true`. This
fixes the dependence calculation of `UnresolvedLookupExpr` for dependent
variable templates.

Fixes #65153 .
  • Loading branch information
LYP951018 committed Oct 17, 2023
1 parent 12a731b commit 4b8b70a
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 21 deletions.
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,10 @@ Bug Fixes to C++ Support
rather than prefer the non-templated constructor as specified in
[standard.group]p3.

- Fixed a crash caused by incorrect handling of dependence on variable templates
with non-type template parameters of reference type. Fixes:
(`#65153 <https://github.com/llvm/llvm-project/issues/65153>`_)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
- Fixed an import failure of recursive friend class template.
Expand Down
8 changes: 6 additions & 2 deletions clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -3191,7 +3191,8 @@ class UnresolvedLookupExpr final
const DeclarationNameInfo &NameInfo, bool RequiresADL,
bool Overloaded,
const TemplateArgumentListInfo *TemplateArgs,
UnresolvedSetIterator Begin, UnresolvedSetIterator End);
UnresolvedSetIterator Begin, UnresolvedSetIterator End,
bool KnownDependent);

UnresolvedLookupExpr(EmptyShell Empty, unsigned NumResults,
bool HasTemplateKWAndArgsInfo);
Expand All @@ -3211,12 +3212,15 @@ class UnresolvedLookupExpr final
const DeclarationNameInfo &NameInfo, bool RequiresADL, bool Overloaded,
UnresolvedSetIterator Begin, UnresolvedSetIterator End);

// After canonicalization, there may be dependent template arguments in
// CanonicalConverted But none of Args is dependent. When any of
// CanonicalConverted dependent, KnownDependent is true.
static UnresolvedLookupExpr *
Create(const ASTContext &Context, CXXRecordDecl *NamingClass,
NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc,
const DeclarationNameInfo &NameInfo, bool RequiresADL,
const TemplateArgumentListInfo *Args, UnresolvedSetIterator Begin,
UnresolvedSetIterator End);
UnresolvedSetIterator End, bool KnownDependent);

static UnresolvedLookupExpr *CreateEmpty(const ASTContext &Context,
unsigned NumResults,
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8395,10 +8395,13 @@ ASTNodeImporter::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) {
if (!ToTemplateKeywordLocOrErr)
return ToTemplateKeywordLocOrErr.takeError();

const bool KnownDependent =
(E->getDependence() & ExprDependence::TypeValue) ==
ExprDependence::TypeValue;
return UnresolvedLookupExpr::Create(
Importer.getToContext(), *ToNamingClassOrErr, *ToQualifierLocOrErr,
*ToTemplateKeywordLocOrErr, ToNameInfo, E->requiresADL(), &ToTAInfo,
ToDecls.begin(), ToDecls.end());
ToDecls.begin(), ToDecls.end(), KnownDependent);
}

return UnresolvedLookupExpr::Create(
Expand Down
16 changes: 8 additions & 8 deletions clang/lib/AST/ExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,10 @@ UnresolvedLookupExpr::UnresolvedLookupExpr(
NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc,
const DeclarationNameInfo &NameInfo, bool RequiresADL, bool Overloaded,
const TemplateArgumentListInfo *TemplateArgs, UnresolvedSetIterator Begin,
UnresolvedSetIterator End)
UnresolvedSetIterator End, bool KnownDependent)
: OverloadExpr(UnresolvedLookupExprClass, Context, QualifierLoc,
TemplateKWLoc, NameInfo, TemplateArgs, Begin, End, false,
false, false),
TemplateKWLoc, NameInfo, TemplateArgs, Begin, End,
KnownDependent, false, false),
NamingClass(NamingClass) {
UnresolvedLookupExprBits.RequiresADL = RequiresADL;
UnresolvedLookupExprBits.Overloaded = Overloaded;
Expand All @@ -380,25 +380,25 @@ UnresolvedLookupExpr *UnresolvedLookupExpr::Create(
void *Mem = Context.Allocate(Size, alignof(UnresolvedLookupExpr));
return new (Mem) UnresolvedLookupExpr(Context, NamingClass, QualifierLoc,
SourceLocation(), NameInfo, RequiresADL,
Overloaded, nullptr, Begin, End);
Overloaded, nullptr, Begin, End, false);
}

UnresolvedLookupExpr *UnresolvedLookupExpr::Create(
const ASTContext &Context, CXXRecordDecl *NamingClass,
NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc,
const DeclarationNameInfo &NameInfo, bool RequiresADL,
const TemplateArgumentListInfo *Args, UnresolvedSetIterator Begin,
UnresolvedSetIterator End) {
UnresolvedSetIterator End, bool KnownDependent) {
assert(Args || TemplateKWLoc.isValid());
unsigned NumResults = End - Begin;
unsigned NumTemplateArgs = Args ? Args->size() : 0;
unsigned Size =
totalSizeToAlloc<DeclAccessPair, ASTTemplateKWAndArgsInfo,
TemplateArgumentLoc>(NumResults, 1, NumTemplateArgs);
void *Mem = Context.Allocate(Size, alignof(UnresolvedLookupExpr));
return new (Mem) UnresolvedLookupExpr(Context, NamingClass, QualifierLoc,
TemplateKWLoc, NameInfo, RequiresADL,
/*Overloaded*/ true, Args, Begin, End);
return new (Mem) UnresolvedLookupExpr(
Context, NamingClass, QualifierLoc, TemplateKWLoc, NameInfo, RequiresADL,
/*Overloaded=*/true, Args, Begin, End, KnownDependent);
}

UnresolvedLookupExpr *UnresolvedLookupExpr::CreateEmpty(
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1299,8 +1299,9 @@ static bool checkTupleLikeDecomposition(Sema &S,
// in the associated namespaces.
Expr *Get = UnresolvedLookupExpr::Create(
S.Context, nullptr, NestedNameSpecifierLoc(), SourceLocation(),
DeclarationNameInfo(GetDN, Loc), /*RequiresADL*/true, &Args,
UnresolvedSetIterator(), UnresolvedSetIterator());
DeclarationNameInfo(GetDN, Loc), /*RequiresADL*/ true, &Args,
UnresolvedSetIterator(), UnresolvedSetIterator(),
/*KnownDependent=*/false);

Expr *Arg = E.get();
E = S.BuildCallExpr(nullptr, Get, Loc, Arg, Loc);
Expand Down
14 changes: 6 additions & 8 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4982,7 +4982,7 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
return ExprError();
}
}

bool KnownDependent = false;
// In C++1y, check variable template ids.
if (R.getAsSingle<VarTemplateDecl>()) {
ExprResult Res = CheckVarTemplateId(SS, R.getLookupNameInfo(),
Expand All @@ -4991,6 +4991,7 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
if (Res.isInvalid() || Res.isUsable())
return Res;
// Result is dependent. Carry on to build an UnresolvedLookupEpxr.
KnownDependent = true;
}

if (R.getAsSingle<ConceptDecl>()) {
Expand All @@ -5002,13 +5003,10 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
// We don't want lookup warnings at this point.
R.suppressDiagnostics();

UnresolvedLookupExpr *ULE
= UnresolvedLookupExpr::Create(Context, R.getNamingClass(),
SS.getWithLocInContext(Context),
TemplateKWLoc,
R.getLookupNameInfo(),
RequiresADL, TemplateArgs,
R.begin(), R.end());
UnresolvedLookupExpr *ULE = UnresolvedLookupExpr::Create(
Context, R.getNamingClass(), SS.getWithLocInContext(Context),
TemplateKWLoc, R.getLookupNameInfo(), RequiresADL, TemplateArgs,
R.begin(), R.end(), KnownDependent);

return ULE;
}
Expand Down
15 changes: 15 additions & 0 deletions clang/test/SemaTemplate/dependent-expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,18 @@ namespace BindingInStmtExpr {
using U = decltype(num_bindings<T>()); // expected-note {{previous}}
using U = N<3>; // expected-error-re {{type alias redefinition with different types ('N<3>' vs {{.*}}N<2>}}
}

namespace PR65153 {
struct A{};

template <const A& T>
const A JoinStringViews = T;

template <int V>
class Builder {
public:
static constexpr A Equal{};
// no crash here
static constexpr auto Val = JoinStringViews<Equal>;
};
} // namespace PR65153

0 comments on commit 4b8b70a

Please sign in to comment.