Skip to content

[clang] Distinguish unresolved templates in UnresolvedLookupExpr #89019

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ Bug Fixes to C++ Support
- Fix a crash when deducing ``auto`` from an invalid dereference (#GH88329).
- Fix a crash in requires expression with templated base class member function. Fixes (#GH84020).
- Placement new initializes typedef array with correct size (#GH41441)
- Fixed a misuse of ``UnresolvedLookupExpr`` for ill-formed templated expressions. Fixes (#GH48673), (#GH63243) and (#GH88832).

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -1116,7 +1116,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
CanQualType BFloat16Ty;
CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3
CanQualType VoidPtrTy, NullPtrTy;
CanQualType DependentTy, OverloadTy, BoundMemberTy, UnknownAnyTy;
CanQualType DependentTy, OverloadTy, BoundMemberTy, UnresolvedTemplateTy,
UnknownAnyTy;
CanQualType BuiltinFnTy;
CanQualType PseudoObjectTy, ARCUnbridgedCastTy;
CanQualType ObjCBuiltinIdTy, ObjCBuiltinClassTy, ObjCBuiltinSelTy;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/BuiltinTypes.def
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,9 @@ PLACEHOLDER_TYPE(Overload, OverloadTy)
// x->foo # if only contains non-static members
PLACEHOLDER_TYPE(BoundMember, BoundMemberTy)

// The type of an unresolved template. Used in UnresolvedLookupExpr.
PLACEHOLDER_TYPE(UnresolvedTemplate, UnresolvedTemplateTy)

// The type of an expression which refers to a pseudo-object,
// such as those introduced by Objective C's @property or
// VS.NET's __property declarations. A placeholder type. The
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/NestedNameSpecifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ class NestedNameSpecifierLoc {
explicit operator bool() const { return Qualifier; }

/// Evaluates true when this nested-name-specifier location is
/// empty.
/// non-empty.
bool hasQualifier() const { return Qualifier; }

/// Retrieve the nested-name-specifier to which this instance
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,9 @@ enum PredefinedTypeIDs {
// \brief WebAssembly reference types with auto numeration
#define WASM_TYPE(Name, Id, SingletonId) PREDEF_TYPE_##Id##_ID,
#include "clang/Basic/WebAssemblyReferenceTypes.def"

/// The placeholder type for unresolved templates.
PREDEF_TYPE_UNRESOLVED_TEMPLATE,
// Sentinel value. Considered a predefined type but not useable as one.
PREDEF_TYPE_LAST_ID
};
Expand All @@ -1108,7 +1111,7 @@ enum PredefinedTypeIDs {
///
/// Type IDs for non-predefined types will start at
/// NUM_PREDEF_TYPE_IDs.
const unsigned NUM_PREDEF_TYPE_IDS = 502;
const unsigned NUM_PREDEF_TYPE_IDS = 503;

// Ensure we do not overrun the predefined types we reserved
// in the enum PredefinedTypeIDs above.
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,9 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target,
// Placeholder type for bound members.
InitBuiltinType(BoundMemberTy, BuiltinType::BoundMember);

// Placeholder type for unresolved templates.
InitBuiltinType(UnresolvedTemplateTy, BuiltinType::UnresolvedTemplate);

// Placeholder type for pseudo-objects.
InitBuiltinType(PseudoObjectTy, BuiltinType::PseudoObject);

Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/NSAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const {
#define WASM_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
#include "clang/Basic/WebAssemblyReferenceTypes.def"
case BuiltinType::BoundMember:
case BuiltinType::UnresolvedTemplate:
case BuiltinType::Dependent:
case BuiltinType::Overload:
case BuiltinType::UnknownAny:
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3381,6 +3381,8 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const {
return "<overloaded function type>";
case BoundMember:
return "<bound member function type>";
case UnresolvedTemplate:
return "<unresolved template type>";
case PseudoObject:
return "<pseudo-object type>";
case Dependent:
Expand Down Expand Up @@ -4673,6 +4675,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
#include "clang/AST/BuiltinTypes.def"
return false;

case BuiltinType::UnresolvedTemplate:
// Dependent types that could instantiate to a pointer type.
case BuiltinType::Dependent:
case BuiltinType::Overload:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/TypeLoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const {
case BuiltinType::NullPtr:
case BuiltinType::Overload:
case BuiltinType::Dependent:
case BuiltinType::UnresolvedTemplate:
case BuiltinType::BoundMember:
case BuiltinType::UnknownAny:
case BuiltinType::ARCUnbridgedCast:
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/Parse/ParseExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,20 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
continue;
}

switch (Tok.getKind()) {
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
#include "clang/Basic/TransformTypeTraits.def"
if (!NextToken().is(tok::l_paren)) {
Tok.setKind(tok::identifier);
Diag(Tok, diag::ext_keyword_as_ident)
<< Tok.getIdentifierInfo()->getName() << 0;
continue;
}
[[fallthrough]];
default:
break;
}

// The rest of the nested-name-specifier possibilities start with
// tok::identifier.
if (Tok.isNot(tok::identifier))
Expand Down
22 changes: 22 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6349,6 +6349,7 @@ static bool isPlaceholderToRemoveAsArg(QualType type) {
#include "clang/AST/BuiltinTypes.def"
return false;

case BuiltinType::UnresolvedTemplate:
// We cannot lower out overload sets; they might validly be resolved
// by the call machinery.
case BuiltinType::Overload:
Expand Down Expand Up @@ -21234,6 +21235,27 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) {
if (!placeholderType) return E;

switch (placeholderType->getKind()) {
case BuiltinType::UnresolvedTemplate: {
auto *ULE = cast<UnresolvedLookupExpr>(E);
const DeclarationNameInfo &NameInfo = ULE->getNameInfo();
// There's only one FoundDecl for UnresolvedTemplate type. See
// BuildTemplateIdExpr.
NamedDecl *Temp = *ULE->decls_begin();
const bool IsTypeAliasTemplateDecl = isa<TypeAliasTemplateDecl>(Temp);

if (NestedNameSpecifierLoc Loc = ULE->getQualifierLoc(); Loc.hasQualifier())
Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template)
<< Loc.getNestedNameSpecifier() << NameInfo.getName().getAsString()
<< Loc.getSourceRange() << IsTypeAliasTemplateDecl;
else
Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template)
<< "" << NameInfo.getName().getAsString() << ULE->getSourceRange()
<< IsTypeAliasTemplateDecl;
Diag(Temp->getLocation(), diag::note_referenced_type_template)
<< IsTypeAliasTemplateDecl;

return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {});
}

// Overloaded expressions.
case BuiltinType::Overload: {
Expand Down
14 changes: 11 additions & 3 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5553,7 +5553,7 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
R.getRepresentativeDecl(), TemplateKWLoc, TemplateArgs);
if (Res.isInvalid() || Res.isUsable())
return Res;
// Result is dependent. Carry on to build an UnresolvedLookupEpxr.
// Result is dependent. Carry on to build an UnresolvedLookupExpr.
KnownDependent = true;
}

Expand All @@ -5571,6 +5571,13 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
TemplateKWLoc, R.getLookupNameInfo(), RequiresADL, TemplateArgs,
R.begin(), R.end(), KnownDependent);

// Model the templates with UnresolvedTemplateTy. The expression should then
// either be transformed in an instantiation or be diagnosed in
// CheckPlaceholderExpr.
if (ULE->getType() == Context.OverloadTy && R.isSingleResult() &&
!R.getFoundDecl()->getAsFunction())
ULE->setType(Context.UnresolvedTemplateTy);

return ULE;
}

Expand Down Expand Up @@ -5609,8 +5616,9 @@ Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS,
Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template)
<< SS.getScopeRep() << NameInfo.getName().getAsString() << SS.getRange()
<< isTypeAliasTemplateDecl;
Diag(Temp->getLocation(), diag::note_referenced_type_template) << 0;
return ExprError();
Diag(Temp->getLocation(), diag::note_referenced_type_template)
<< isTypeAliasTemplateDecl;
return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {});
};

if (ClassTemplateDecl *Temp = R.getAsSingle<ClassTemplateDecl>())
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Serialization/ASTCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) {
case BuiltinType::Overload:
ID = PREDEF_TYPE_OVERLOAD_ID;
break;
case BuiltinType::UnresolvedTemplate:
ID = PREDEF_TYPE_UNRESOLVED_TEMPLATE;
break;
case BuiltinType::BoundMember:
ID = PREDEF_TYPE_BOUND_MEMBER;
break;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7311,6 +7311,9 @@ QualType ASTReader::GetType(TypeID ID) {
case PREDEF_TYPE_OVERLOAD_ID:
T = Context.OverloadTy;
break;
case PREDEF_TYPE_UNRESOLVED_TEMPLATE:
T = Context.UnresolvedTemplateTy;
break;
case PREDEF_TYPE_BOUND_MEMBER:
T = Context.BoundMemberTy;
break;
Expand Down
2 changes: 1 addition & 1 deletion clang/test/SemaCXX/PR62533.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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

template<typename T, typename V>
Expand Down
90 changes: 90 additions & 0 deletions clang/test/SemaTemplate/template-id-expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,93 @@ class E {
#endif
template<typename T> using D = int; // expected-note {{declared here}}
E<D> ed; // expected-note {{instantiation of}}

namespace non_functions {

#if __cplusplus >= 201103L
namespace PR88832 {
template <typename T> struct O {
static const T v = 0;
};

struct P {
template <typename T> using I = typename O<T>::v; // #TypeAlias
};

struct Q {
template <typename T> int foo() {
return T::template I<int>;
// expected-error@-1 {{'P::I' is expected to be a non-type template, but instantiated to a type alias template}}
// expected-note@#TypeAlias {{type alias template declared here}}
}
};

int bar() {
return Q().foo<P>(); // expected-note-re {{function template specialization {{.*}} requested here}}
}

} // namespace PR88832
#endif

namespace PR63243 {

namespace std {
template <class T> struct add_pointer { // #add_pointer
};
} // namespace std

class A {};

int main() {
std::__add_pointer<A>::type ptr;
// expected-warning@-1 {{keyword '__add_pointer' will be made available as an identifier here}}
// expected-error@-2 {{no template named '__add_pointer'}}
// expected-note@#add_pointer {{'add_pointer' declared here}}
// expected-error-re@-4 {{no type named 'type' in '{{.*}}std::add_pointer<{{.*}}A>'}}

__add_pointer<A>::type ptr2;
// expected-error@-1 {{no template named '__add_pointer'}}
// expected-error-re@-2 {{no type named 'type' in '{{.*}}std::add_pointer<{{.*}}A>'}}
// expected-note@#add_pointer {{'std::add_pointer' declared here}}
}

} // namespace PR63243

namespace PR48673 {

template <typename T> struct C {
template <int TT> class Type {}; // #ClassTemplate
};

template <typename T1> struct A {

template <typename T2>
void foo(T2) {}

void foo() {
C<T1>::template Type<2>;
// expected-error@-1 {{'C<float>::Type' is expected to be a non-type template, but instantiated to a class template}}}
// expected-note@#ClassTemplate {{class template declared here}}

foo(C<T1>::Type<2>); // expected-error {{expected expression}}

foo(C<T1>::template Type<2>);
// expected-error@-1 {{'C<float>::Type' is expected to be a non-type template, but instantiated to a class template}}
// expected-note@#ClassTemplate {{class template declared here}}

foo(C<T1>::template Type<2>());
// expected-error@-1 {{'C<float>::Type' is expected to be a non-type template, but instantiated to a class template}}
// expected-error@-2 {{called object type '<dependent type>' is not a function or function pointer}}
// expected-note@#ClassTemplate {{class template declared here}}

foo(typename C<T1>::template Type<2>());
}
};

void test() {
A<float>().foo(); // expected-note-re {{instantiation of member function {{.*}} requested here}}
}

} // namespace PR48673

}
Loading