Skip to content

Commit 7a484d3

Browse files
authored
[clang] Distinguish unresolved templates in UnresolvedLookupExpr (#89019)
This patch revolves around the misuse of UnresolvedLookupExpr in BuildTemplateIdExpr. Basically, we build up an UnresolvedLookupExpr not only for function overloads but for "unresolved" templates wherever we need an expression for template decls. For example, a dependent VarTemplateDecl can be wrapped with such an expression before template instantiation. (See 6170072) Also, one important thing is that UnresolvedLookupExpr uses a "canonical" QualType to describe the containing unresolved decls: a DependentTy is for dependent expressions and an OverloadTy otherwise. Therefore, this modeling for non-dependent templates leaves a problem in that the expression is marked and perceived as if describing overload functions. The consumer then expects functions for every such expression, although the fact is the reverse. Hence, we run into crashes. As to the patch, I added a new canonical type "UnresolvedTemplateTy" to model these cases. Given that we have been using this model (intentionally or accidentally) and it is pretty baked in throughout the code, I think extending the role of UnresolvedLookupExpr is reasonable. Further, I added some diagnostics for the direct occurrence of these expressions, which are supposed to be ill-formed. As a bonus, this patch also fixes some typos in the diagnostics and creates RecoveryExprs rather than nothing in the hope of a better error-recovery for clangd. Fixes #88832 Fixes #63243 Fixes #48673
1 parent ffc9a30 commit 7a484d3

File tree

16 files changed

+185
-6
lines changed

16 files changed

+185
-6
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,8 @@ Bug Fixes to C++ Support
681681
whose type is `decltype(auto)`. Fixes (#GH68885).
682682
- Clang now correctly treats the noexcept-specifier of a friend function to be a complete-class context.
683683
- Fix an assertion failure when parsing an invalid members of an anonymous class. (#GH85447)
684+
- Fixed a misuse of ``UnresolvedLookupExpr`` for ill-formed templated expressions. Fixes (#GH48673), (#GH63243)
685+
and (#GH88832).
684686

685687
Bug Fixes to AST Handling
686688
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/ASTContext.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1116,7 +1116,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
11161116
CanQualType BFloat16Ty;
11171117
CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3
11181118
CanQualType VoidPtrTy, NullPtrTy;
1119-
CanQualType DependentTy, OverloadTy, BoundMemberTy, UnknownAnyTy;
1119+
CanQualType DependentTy, OverloadTy, BoundMemberTy, UnresolvedTemplateTy,
1120+
UnknownAnyTy;
11201121
CanQualType BuiltinFnTy;
11211122
CanQualType PseudoObjectTy, ARCUnbridgedCastTy;
11221123
CanQualType ObjCBuiltinIdTy, ObjCBuiltinClassTy, ObjCBuiltinSelTy;

clang/include/clang/AST/BuiltinTypes.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,9 @@ PLACEHOLDER_TYPE(Overload, OverloadTy)
285285
// x->foo # if only contains non-static members
286286
PLACEHOLDER_TYPE(BoundMember, BoundMemberTy)
287287

288+
// The type of an unresolved template. Used in UnresolvedLookupExpr.
289+
PLACEHOLDER_TYPE(UnresolvedTemplate, UnresolvedTemplateTy)
290+
288291
// The type of an expression which refers to a pseudo-object,
289292
// such as those introduced by Objective C's @property or
290293
// VS.NET's __property declarations. A placeholder type. The

clang/include/clang/AST/ExprCXX.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3163,8 +3163,30 @@ class OverloadExpr : public Expr {
31633163
/// This arises in several ways:
31643164
/// * we might be waiting for argument-dependent lookup;
31653165
/// * the name might resolve to an overloaded function;
3166+
/// * the name might resolve to a non-function template; for example, in the
3167+
/// following snippet, the return expression of the member function
3168+
/// 'foo()' might remain unresolved until instantiation:
3169+
///
3170+
/// \code
3171+
/// struct P {
3172+
/// template <class T> using I = T;
3173+
/// };
3174+
///
3175+
/// struct Q {
3176+
/// template <class T> int foo() {
3177+
/// return T::template I<int>;
3178+
/// }
3179+
/// };
3180+
/// \endcode
3181+
///
3182+
/// ...which is distinct from modeling function overloads, and therefore we use
3183+
/// a different builtin type 'UnresolvedTemplate' to avoid confusion. This is
3184+
/// done in Sema::BuildTemplateIdExpr.
3185+
///
31663186
/// and eventually:
31673187
/// * the lookup might have included a function template.
3188+
/// * the unresolved template gets transformed in an instantiation or gets
3189+
/// diagnosed for its direct use.
31683190
///
31693191
/// These never include UnresolvedUsingValueDecls, which are always class
31703192
/// members and therefore appear only in UnresolvedMemberLookupExprs.

clang/include/clang/Serialization/ASTBitCodes.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,9 @@ enum PredefinedTypeIDs {
10911091
// \brief WebAssembly reference types with auto numeration
10921092
#define WASM_TYPE(Name, Id, SingletonId) PREDEF_TYPE_##Id##_ID,
10931093
#include "clang/Basic/WebAssemblyReferenceTypes.def"
1094+
1095+
/// The placeholder type for unresolved templates.
1096+
PREDEF_TYPE_UNRESOLVED_TEMPLATE,
10941097
// Sentinel value. Considered a predefined type but not useable as one.
10951098
PREDEF_TYPE_LAST_ID
10961099
};
@@ -1100,7 +1103,7 @@ enum PredefinedTypeIDs {
11001103
///
11011104
/// Type IDs for non-predefined types will start at
11021105
/// NUM_PREDEF_TYPE_IDs.
1103-
const unsigned NUM_PREDEF_TYPE_IDS = 502;
1106+
const unsigned NUM_PREDEF_TYPE_IDS = 503;
11041107

11051108
// Ensure we do not overrun the predefined types we reserved
11061109
// in the enum PredefinedTypeIDs above.

clang/lib/AST/ASTContext.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,9 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target,
13071307
// Placeholder type for bound members.
13081308
InitBuiltinType(BoundMemberTy, BuiltinType::BoundMember);
13091309

1310+
// Placeholder type for unresolved templates.
1311+
InitBuiltinType(UnresolvedTemplateTy, BuiltinType::UnresolvedTemplate);
1312+
13101313
// Placeholder type for pseudo-objects.
13111314
InitBuiltinType(PseudoObjectTy, BuiltinType::PseudoObject);
13121315

clang/lib/AST/NSAPI.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const {
454454
#define WASM_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
455455
#include "clang/Basic/WebAssemblyReferenceTypes.def"
456456
case BuiltinType::BoundMember:
457+
case BuiltinType::UnresolvedTemplate:
457458
case BuiltinType::Dependent:
458459
case BuiltinType::Overload:
459460
case BuiltinType::UnknownAny:

clang/lib/AST/Type.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3393,6 +3393,8 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const {
33933393
return "<overloaded function type>";
33943394
case BoundMember:
33953395
return "<bound member function type>";
3396+
case UnresolvedTemplate:
3397+
return "<unresolved template type>";
33963398
case PseudoObject:
33973399
return "<pseudo-object type>";
33983400
case Dependent:
@@ -4685,6 +4687,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
46854687
#include "clang/AST/BuiltinTypes.def"
46864688
return false;
46874689

4690+
case BuiltinType::UnresolvedTemplate:
46884691
// Dependent types that could instantiate to a pointer type.
46894692
case BuiltinType::Dependent:
46904693
case BuiltinType::Overload:

clang/lib/AST/TypeLoc.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const {
399399
case BuiltinType::NullPtr:
400400
case BuiltinType::Overload:
401401
case BuiltinType::Dependent:
402+
case BuiltinType::UnresolvedTemplate:
402403
case BuiltinType::BoundMember:
403404
case BuiltinType::UnknownAny:
404405
case BuiltinType::ARCUnbridgedCast:

clang/lib/Parse/ParseExprCXX.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,20 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
407407
continue;
408408
}
409409

410+
switch (Tok.getKind()) {
411+
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
412+
#include "clang/Basic/TransformTypeTraits.def"
413+
if (!NextToken().is(tok::l_paren)) {
414+
Tok.setKind(tok::identifier);
415+
Diag(Tok, diag::ext_keyword_as_ident)
416+
<< Tok.getIdentifierInfo()->getName() << 0;
417+
continue;
418+
}
419+
[[fallthrough]];
420+
default:
421+
break;
422+
}
423+
410424
// The rest of the nested-name-specifier possibilities start with
411425
// tok::identifier.
412426
if (Tok.isNot(tok::identifier))

clang/lib/Sema/SemaExpr.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6345,6 +6345,7 @@ static bool isPlaceholderToRemoveAsArg(QualType type) {
63456345
#include "clang/AST/BuiltinTypes.def"
63466346
return false;
63476347

6348+
case BuiltinType::UnresolvedTemplate:
63486349
// We cannot lower out overload sets; they might validly be resolved
63496350
// by the call machinery.
63506351
case BuiltinType::Overload:
@@ -21253,6 +21254,27 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) {
2125321254
if (!placeholderType) return E;
2125421255

2125521256
switch (placeholderType->getKind()) {
21257+
case BuiltinType::UnresolvedTemplate: {
21258+
auto *ULE = cast<UnresolvedLookupExpr>(E);
21259+
const DeclarationNameInfo &NameInfo = ULE->getNameInfo();
21260+
// There's only one FoundDecl for UnresolvedTemplate type. See
21261+
// BuildTemplateIdExpr.
21262+
NamedDecl *Temp = *ULE->decls_begin();
21263+
const bool IsTypeAliasTemplateDecl = isa<TypeAliasTemplateDecl>(Temp);
21264+
21265+
if (NestedNameSpecifierLoc Loc = ULE->getQualifierLoc(); Loc.hasQualifier())
21266+
Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template)
21267+
<< Loc.getNestedNameSpecifier() << NameInfo.getName().getAsString()
21268+
<< Loc.getSourceRange() << IsTypeAliasTemplateDecl;
21269+
else
21270+
Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template)
21271+
<< "" << NameInfo.getName().getAsString() << ULE->getSourceRange()
21272+
<< IsTypeAliasTemplateDecl;
21273+
Diag(Temp->getLocation(), diag::note_referenced_type_template)
21274+
<< IsTypeAliasTemplateDecl;
21275+
21276+
return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {});
21277+
}
2125621278

2125721279
// Overloaded expressions.
2125821280
case BuiltinType::Overload: {

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5554,7 +5554,7 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
55545554
R.getRepresentativeDecl(), TemplateKWLoc, TemplateArgs);
55555555
if (Res.isInvalid() || Res.isUsable())
55565556
return Res;
5557-
// Result is dependent. Carry on to build an UnresolvedLookupEpxr.
5557+
// Result is dependent. Carry on to build an UnresolvedLookupExpr.
55585558
KnownDependent = true;
55595559
}
55605560

@@ -5572,6 +5572,13 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
55725572
TemplateKWLoc, R.getLookupNameInfo(), RequiresADL, TemplateArgs,
55735573
R.begin(), R.end(), KnownDependent);
55745574

5575+
// Model the templates with UnresolvedTemplateTy. The expression should then
5576+
// either be transformed in an instantiation or be diagnosed in
5577+
// CheckPlaceholderExpr.
5578+
if (ULE->getType() == Context.OverloadTy && R.isSingleResult() &&
5579+
!R.getFoundDecl()->getAsFunction())
5580+
ULE->setType(Context.UnresolvedTemplateTy);
5581+
55755582
return ULE;
55765583
}
55775584

@@ -5608,8 +5615,9 @@ Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS,
56085615
Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template)
56095616
<< SS.getScopeRep() << NameInfo.getName().getAsString() << SS.getRange()
56105617
<< isTypeAliasTemplateDecl;
5611-
Diag(Temp->getLocation(), diag::note_referenced_type_template) << 0;
5612-
return ExprError();
5618+
Diag(Temp->getLocation(), diag::note_referenced_type_template)
5619+
<< isTypeAliasTemplateDecl;
5620+
return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {});
56135621
};
56145622

56155623
if (ClassTemplateDecl *Temp = R.getAsSingle<ClassTemplateDecl>())

clang/lib/Serialization/ASTCommon.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) {
186186
case BuiltinType::Overload:
187187
ID = PREDEF_TYPE_OVERLOAD_ID;
188188
break;
189+
case BuiltinType::UnresolvedTemplate:
190+
ID = PREDEF_TYPE_UNRESOLVED_TEMPLATE;
191+
break;
189192
case BuiltinType::BoundMember:
190193
ID = PREDEF_TYPE_BOUND_MEMBER;
191194
break;

clang/lib/Serialization/ASTReader.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7320,6 +7320,9 @@ QualType ASTReader::GetType(TypeID ID) {
73207320
case PREDEF_TYPE_OVERLOAD_ID:
73217321
T = Context.OverloadTy;
73227322
break;
7323+
case PREDEF_TYPE_UNRESOLVED_TEMPLATE:
7324+
T = Context.UnresolvedTemplateTy;
7325+
break;
73237326
case PREDEF_TYPE_BOUND_MEMBER:
73247327
T = Context.BoundMemberTy;
73257328
break;

clang/test/SemaCXX/PR62533.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
template<typename T>
44
struct test {
5-
template<typename> using fun_diff = char; // expected-note 2{{class template declared here}}
5+
template<typename> using fun_diff = char; // expected-note 2{{type alias template declared here}}
66
};
77

88
template<typename T, typename V>

clang/test/SemaTemplate/template-id-expr.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,93 @@ class E {
186186
#endif
187187
template<typename T> using D = int; // expected-note {{declared here}}
188188
E<D> ed; // expected-note {{instantiation of}}
189+
190+
namespace non_functions {
191+
192+
#if __cplusplus >= 201103L
193+
namespace PR88832 {
194+
template <typename T> struct O {
195+
static const T v = 0;
196+
};
197+
198+
struct P {
199+
template <typename T> using I = typename O<T>::v; // #TypeAlias
200+
};
201+
202+
struct Q {
203+
template <typename T> int foo() {
204+
return T::template I<int>;
205+
// expected-error@-1 {{'P::I' is expected to be a non-type template, but instantiated to a type alias template}}
206+
// expected-note@#TypeAlias {{type alias template declared here}}
207+
}
208+
};
209+
210+
int bar() {
211+
return Q().foo<P>(); // expected-note-re {{function template specialization {{.*}} requested here}}
212+
}
213+
214+
} // namespace PR88832
215+
#endif
216+
217+
namespace PR63243 {
218+
219+
namespace std {
220+
template <class T> struct add_pointer { // #add_pointer
221+
};
222+
} // namespace std
223+
224+
class A {};
225+
226+
int main() {
227+
std::__add_pointer<A>::type ptr;
228+
// expected-warning@-1 {{keyword '__add_pointer' will be made available as an identifier here}}
229+
// expected-error@-2 {{no template named '__add_pointer'}}
230+
// expected-note@#add_pointer {{'add_pointer' declared here}}
231+
// expected-error-re@-4 {{no type named 'type' in '{{.*}}std::add_pointer<{{.*}}A>'}}
232+
233+
__add_pointer<A>::type ptr2;
234+
// expected-error@-1 {{no template named '__add_pointer'}}
235+
// expected-error-re@-2 {{no type named 'type' in '{{.*}}std::add_pointer<{{.*}}A>'}}
236+
// expected-note@#add_pointer {{'std::add_pointer' declared here}}
237+
}
238+
239+
} // namespace PR63243
240+
241+
namespace PR48673 {
242+
243+
template <typename T> struct C {
244+
template <int TT> class Type {}; // #ClassTemplate
245+
};
246+
247+
template <typename T1> struct A {
248+
249+
template <typename T2>
250+
void foo(T2) {}
251+
252+
void foo() {
253+
C<T1>::template Type<2>;
254+
// expected-error@-1 {{'C<float>::Type' is expected to be a non-type template, but instantiated to a class template}}}
255+
// expected-note@#ClassTemplate {{class template declared here}}
256+
257+
foo(C<T1>::Type<2>); // expected-error {{expected expression}}
258+
259+
foo(C<T1>::template Type<2>);
260+
// expected-error@-1 {{'C<float>::Type' is expected to be a non-type template, but instantiated to a class template}}
261+
// expected-note@#ClassTemplate {{class template declared here}}
262+
263+
foo(C<T1>::template Type<2>());
264+
// expected-error@-1 {{'C<float>::Type' is expected to be a non-type template, but instantiated to a class template}}
265+
// expected-error@-2 {{called object type '<dependent type>' is not a function or function pointer}}
266+
// expected-note@#ClassTemplate {{class template declared here}}
267+
268+
foo(typename C<T1>::template Type<2>());
269+
}
270+
};
271+
272+
void test() {
273+
A<float>().foo(); // expected-note-re {{instantiation of member function {{.*}} requested here}}
274+
}
275+
276+
} // namespace PR48673
277+
278+
}

0 commit comments

Comments
 (0)