diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 54b58b1ae99fb..b146a9b56884a 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -681,6 +681,8 @@ Bug Fixes to C++ Support whose type is `decltype(auto)`. Fixes (#GH68885). - Clang now correctly treats the noexcept-specifier of a friend function to be a complete-class context. - Fix an assertion failure when parsing an invalid members of an anonymous class. (#GH85447) +- Fixed a misuse of ``UnresolvedLookupExpr`` for ill-formed templated expressions. Fixes (#GH48673), (#GH63243) + and (#GH88832). Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 6dbd06251ddad..e03b112194786 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1116,7 +1116,8 @@ class ASTContext : public RefCountedBase { 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; diff --git a/clang/include/clang/AST/BuiltinTypes.def b/clang/include/clang/AST/BuiltinTypes.def index 0a36fdc5d9c0f..444be4311a743 100644 --- a/clang/include/clang/AST/BuiltinTypes.def +++ b/clang/include/clang/AST/BuiltinTypes.def @@ -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 diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index ab3f810b45192..fac65628ffede 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -3163,8 +3163,30 @@ class OverloadExpr : public Expr { /// This arises in several ways: /// * we might be waiting for argument-dependent lookup; /// * the name might resolve to an overloaded function; +/// * the name might resolve to a non-function template; for example, in the +/// following snippet, the return expression of the member function +/// 'foo()' might remain unresolved until instantiation: +/// +/// \code +/// struct P { +/// template using I = T; +/// }; +/// +/// struct Q { +/// template int foo() { +/// return T::template I; +/// } +/// }; +/// \endcode +/// +/// ...which is distinct from modeling function overloads, and therefore we use +/// a different builtin type 'UnresolvedTemplate' to avoid confusion. This is +/// done in Sema::BuildTemplateIdExpr. +/// /// and eventually: /// * the lookup might have included a function template. +/// * the unresolved template gets transformed in an instantiation or gets +/// diagnosed for its direct use. /// /// These never include UnresolvedUsingValueDecls, which are always class /// members and therefore appear only in UnresolvedMemberLookupExprs. diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index a8df5a0bda085..b7aaceacc0062 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1091,6 +1091,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 }; @@ -1100,7 +1103,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. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 699721aabdb5e..5f96e86f803a8 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1307,6 +1307,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); diff --git a/clang/lib/AST/NSAPI.cpp b/clang/lib/AST/NSAPI.cpp index 6f586173edb02..2d16237f5325a 100644 --- a/clang/lib/AST/NSAPI.cpp +++ b/clang/lib/AST/NSAPI.cpp @@ -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: diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 68e81f45b4c28..2385c5e02cb26 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3393,6 +3393,8 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const { return ""; case BoundMember: return ""; + case UnresolvedTemplate: + return ""; case PseudoObject: return ""; case Dependent: @@ -4685,6 +4687,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: diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index ce45b47d5cfea..9dd90d9bf4e54 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -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: diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 0d2ad980696fc..825031da358ad 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -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)) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 2557b1af8f024..b1322f30fa6b6 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6345,6 +6345,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: @@ -21253,6 +21254,27 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) { if (!placeholderType) return E; switch (placeholderType->getKind()) { + case BuiltinType::UnresolvedTemplate: { + auto *ULE = cast(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(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: { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 989f3995ca599..e647ac267ab39 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5554,7 +5554,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; } @@ -5572,6 +5572,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; } @@ -5608,8 +5615,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()) diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index e017f5bdb4885..63c5140086d8e 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -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; diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 6d121f4ab80e8..36ec6d6b2eb7e 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -7320,6 +7320,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; diff --git a/clang/test/SemaCXX/PR62533.cpp b/clang/test/SemaCXX/PR62533.cpp index 920ea54d4b00e..0753156813f8e 100644 --- a/clang/test/SemaCXX/PR62533.cpp +++ b/clang/test/SemaCXX/PR62533.cpp @@ -2,7 +2,7 @@ template struct test { - template using fun_diff = char; // expected-note 2{{class template declared here}} + template using fun_diff = char; // expected-note 2{{type alias template declared here}} }; template diff --git a/clang/test/SemaTemplate/template-id-expr.cpp b/clang/test/SemaTemplate/template-id-expr.cpp index 0555d8b94504f..ce40aade9cf17 100644 --- a/clang/test/SemaTemplate/template-id-expr.cpp +++ b/clang/test/SemaTemplate/template-id-expr.cpp @@ -186,3 +186,93 @@ class E { #endif template using D = int; // expected-note {{declared here}} E ed; // expected-note {{instantiation of}} + +namespace non_functions { + +#if __cplusplus >= 201103L +namespace PR88832 { +template struct O { + static const T v = 0; +}; + +struct P { + template using I = typename O::v; // #TypeAlias +}; + +struct Q { + template int foo() { + return T::template I; + // 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

(); // expected-note-re {{function template specialization {{.*}} requested here}} +} + +} // namespace PR88832 +#endif + +namespace PR63243 { + +namespace std { +template struct add_pointer { // #add_pointer +}; +} // namespace std + +class A {}; + +int main() { + std::__add_pointer::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::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 struct C { + template class Type {}; // #ClassTemplate +}; + +template struct A { + + template + void foo(T2) {} + + void foo() { + C::template Type<2>; + // expected-error@-1 {{'C::Type' is expected to be a non-type template, but instantiated to a class template}}} + // expected-note@#ClassTemplate {{class template declared here}} + + foo(C::Type<2>); // expected-error {{expected expression}} + + foo(C::template Type<2>); + // expected-error@-1 {{'C::Type' is expected to be a non-type template, but instantiated to a class template}} + // expected-note@#ClassTemplate {{class template declared here}} + + foo(C::template Type<2>()); + // expected-error@-1 {{'C::Type' is expected to be a non-type template, but instantiated to a class template}} + // expected-error@-2 {{called object type '' is not a function or function pointer}} + // expected-note@#ClassTemplate {{class template declared here}} + + foo(typename C::template Type<2>()); + } +}; + +void test() { + A().foo(); // expected-note-re {{instantiation of member function {{.*}} requested here}} +} + +} // namespace PR48673 + +}