Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -4456,6 +4456,11 @@ "%0 in %1">; def note_using_value_decl_missing_typename : Note< "add 'typename' to treat this using declaration as a type">; +def warn_cxx17_compat_implicit_typename : Warning<"use of implicit 'typename' is " + "incompatible with C++ standards before C++2a">, InGroup, + DefaultIgnore; +def ext_implicit_typename : ExtWarn<"implicit 'typename' is a C++2a extension">, + InGroup; def err_template_kw_refers_to_non_template : Error< "%0 following the 'template' keyword does not refer to a template">; Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -1994,7 +1994,8 @@ DSC_template_param, // template parameter context DSC_template_type_arg, // template type argument context DSC_objc_method_result, // ObjC method result context, enables 'instancetype' - DSC_condition // condition declaration context + DSC_condition, // condition declaration context + DSC_block, // declaration within a block }; /// Is this a context in which we are parsing just a type-specifier (or @@ -2007,6 +2008,7 @@ case DeclSpecContext::DSC_top_level: case DeclSpecContext::DSC_objc_method_result: case DeclSpecContext::DSC_condition: + case DeclSpecContext::DSC_block: return false; case DeclSpecContext::DSC_template_type_arg: @@ -2028,6 +2030,7 @@ case DeclSpecContext::DSC_top_level: case DeclSpecContext::DSC_condition: case DeclSpecContext::DSC_type_specifier: + case DeclSpecContext::DSC_block: return true; case DeclSpecContext::DSC_objc_method_result: @@ -2039,6 +2042,27 @@ llvm_unreachable("Missing DeclSpecContext case"); } + /// Context where an implicit typename is allowed. + static bool isImplicitTypenameContext(DeclSpecContext DSC) { + switch (DSC) { + case DeclSpecContext::DSC_class: + case DeclSpecContext::DSC_type_specifier: + case DeclSpecContext::DSC_trailing: + case DeclSpecContext::DSC_alias_declaration: + case DeclSpecContext::DSC_top_level: + case DeclSpecContext::DSC_template_param: + case DeclSpecContext::DSC_template_type_arg: + case DeclSpecContext::DSC_normal: + return true; + + case DeclSpecContext::DSC_objc_method_result: + case DeclSpecContext::DSC_condition: + case DeclSpecContext::DSC_block: + return false; + } + llvm_unreachable("Missing DeclSpecContext case"); + } + /// Information on a C++0x for-range-initializer found while parsing a /// declaration which turns out to be a for-range-declaration. struct ForRangeInit { Index: include/clang/Sema/Scope.h =================================================================== --- include/clang/Sema/Scope.h +++ include/clang/Sema/Scope.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_SEMA_SCOPE_H #define LLVM_CLANG_SEMA_SCOPE_H +#include "clang/AST/DeclBase.h" #include "clang/Basic/Diagnostic.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallPtrSet.h" @@ -30,7 +31,6 @@ namespace clang { class Decl; -class DeclContext; class UsingDirectiveDecl; class VarDecl; @@ -444,6 +444,13 @@ return getFlags() & Scope::CompoundStmtScope; } + /// Determine whether this scope is a namespace (or TU) scope. + bool isNamespaceOrTranslationUnitScope() const { + if (Entity) + return Entity->isFileContext(); + return false; + } + /// Returns if rhs has a higher scope depth than this. /// /// The caller is responsible for calling this only if one of the two scopes Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -1698,6 +1698,7 @@ bool IsCtorOrDtorName = false, bool WantNontrivialTypeSourceInfo = false, bool IsClassTemplateDeductionContext = true, + bool AllowImplicitTypename = false, IdentifierInfo **CorrectedII = nullptr); TypeSpecifierType isTagName(IdentifierInfo &II, Scope *S); bool isMicrosoftMissingTypename(const CXXScopeSpec *SS, Scope *S); @@ -6486,10 +6487,11 @@ /// \param SS the nested-name-specifier following the typename (e.g., 'T::'). /// \param II the identifier we're retrieving (e.g., 'type' in the example). /// \param IdLoc the location of the identifier. - TypeResult - ActOnTypenameType(Scope *S, SourceLocation TypenameLoc, - const CXXScopeSpec &SS, const IdentifierInfo &II, - SourceLocation IdLoc); + /// \param IsImplicitTypename whether the 'typename' is implicit. + TypeResult ActOnTypenameType(Scope *S, SourceLocation TypenameLoc, + const CXXScopeSpec &SS, const IdentifierInfo &II, + SourceLocation IdLoc, + bool IsImplicitTypename = false); /// Called when the parser has parsed a C++ typename /// specifier that ends in a template-id, e.g., Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -2691,6 +2691,16 @@ if (Context == DeclaratorContext::AliasDeclContext || Context == DeclaratorContext::AliasTemplateContext) return DeclSpecContext::DSC_alias_declaration; + if (Context == DeclaratorContext::BlockContext || + Context == DeclaratorContext::ForContext || + Context == DeclaratorContext::InitStmtContext || + Context == DeclaratorContext::LambdaExprContext || + Context == DeclaratorContext::CXXCatchContext) + return DeclSpecContext::DSC_block; + if (Context == DeclaratorContext::TypeNameContext) + return DeclSpecContext::DSC_type_specifier; + if (Context == DeclaratorContext::ConditionContext) + return DeclSpecContext::DSC_condition; return DeclSpecContext::DSC_normal; } @@ -3101,7 +3111,8 @@ getCurScope(), &SS, false, false, nullptr, /*IsCtorOrDtorName=*/false, /*WantNonTrivialSourceInfo=*/true, - isClassTemplateDeductionContext(DSContext)); + isClassTemplateDeductionContext(DSContext), + isImplicitTypenameContext(DSContext)); // If the referenced identifier is not a type, then this declspec is // erroneous: We already checked about that it has no type specifier, and @@ -3231,7 +3242,6 @@ if (DSContext == DeclSpecContext::DSC_objc_method_result && isObjCInstancetype()) { ParsedType TypeRep = Actions.ActOnObjCInstanceType(Loc); - assert(TypeRep); isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID, TypeRep, Policy); if (isInvalid) @@ -5828,6 +5838,24 @@ TentativelyDeclaredIdentifiers.push_back(D.getIdentifier()); bool IsFunctionDecl = isCXXFunctionDeclarator(&IsAmbiguous); TentativelyDeclaredIdentifiers.pop_back(); + + if (!IsFunctionDecl && IsAmbiguous) { + // We have an ambiguity between a function and a variable: + // template void N::f(T::type) [...] + // but only for qualified names. + if (D.getCXXScopeSpec().isSet()) { + LookupResult LR(Actions, D.getIdentifier(), D.getBeginLoc(), + Sema::LookupOrdinaryName, + Sema::ForVisibleRedeclaration); + DeclContext *DC = Actions.computeDeclContext(D.getCXXScopeSpec()); + assert(DC && "couldn't find decl context of qualified name"); + Actions.LookupQualifiedName(LR, DC); + + for (Decl *D : LR) + IsFunctionDecl = + isa(D) || isa(D); + } + } if (!IsFunctionDecl) break; } Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -1175,7 +1175,8 @@ *Id, IdLoc, getCurScope(), &SS, /*IsClassName=*/true, false, nullptr, /*IsCtorOrDtorName=*/false, /*NonTrivialTypeSourceInfo=*/true, - /*IsClassTemplateDeductionContext*/ false, &CorrectedII); + /*IsClassTemplateDeductionContext*/ false, + /*AllowImplicitTypename*/true, &CorrectedII); if (!Type) { Diag(IdLoc, diag::err_expected_class_name); return true; Index: lib/Parse/ParseExprCXX.cpp =================================================================== --- lib/Parse/ParseExprCXX.cpp +++ lib/Parse/ParseExprCXX.cpp @@ -1351,7 +1351,8 @@ // Parse the common declaration-specifiers piece. DeclSpec DS(AttrFactory); - ParseSpecifierQualifierList(DS); + ParseSpecifierQualifierList(DS, /*AccessSpecifier=*/AS_none, + DeclSpecContext::DSC_type_specifier); // Parse the abstract-declarator, if present. Declarator DeclaratorInfo(DS, DeclaratorContext::TypeNameContext); Index: lib/Parse/ParseTentative.cpp =================================================================== --- lib/Parse/ParseTentative.cpp +++ lib/Parse/ParseTentative.cpp @@ -1788,9 +1788,14 @@ // and can appear next in a function definition. This must be a function // declarator. TPR = TPResult::True; - else if (InvalidAsDeclaration) + else if (InvalidAsDeclaration) { // Use the absence of 'typename' as a tie-breaker. TPR = TPResult::False; + // But still mark the declaration as ambiguous, as the tie-breakter is + // not perfect. + if (IsAmbiguous) + *IsAmbiguous = true; + } } } Index: lib/Parse/Parser.cpp =================================================================== --- lib/Parse/Parser.cpp +++ lib/Parse/Parser.cpp @@ -1773,8 +1773,9 @@ *Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), &SS, false, NextToken().is(tok::period), nullptr, /*IsCtorOrDtorName=*/false, - /*NonTrivialTypeSourceInfo*/true, - /*IsClassTemplateDeductionContext*/true)) { + /*NonTrivialTypeSourceInfo*/ true, + /*IsClassTemplateDeductionContext*/ true, + /*AllowImplicitTypename*/false)) { SourceLocation BeginLoc = Tok.getLocation(); if (SS.isNotEmpty()) // it was a C++ qualified type name. BeginLoc = SS.getBeginLoc(); Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -281,6 +281,7 @@ bool IsCtorOrDtorName, bool WantNontrivialTypeSourceInfo, bool IsClassTemplateDeductionContext, + bool AllowImplicitTypename, IdentifierInfo **CorrectedII) { // FIXME: Consider allowing this outside C++1z mode as an extension. bool AllowDeducedTemplate = IsClassTemplateDeductionContext && @@ -307,17 +308,37 @@ // // We therefore do not perform any name lookup if the result would // refer to a member of an unknown specialization. - if (!isClassName && !IsCtorOrDtorName) + // In C++2a, in several contexts a 'typename' is not required. Also + // allow this as an extension. + if (!AllowImplicitTypename && !isClassName && !IsCtorOrDtorName) return nullptr; + bool IsImplicitTypename = !isClassName && !IsCtorOrDtorName; + + // We need to delay this diagnostic for function parameters, because + // at that point we don't know whether we have a function-definition + // (where implicit typename is allowed) or a unqualified function + // declaration (where implicit typename is not allowed). + if (IsImplicitTypename && !S->isFunctionPrototypeScope()) { + SourceLocation QualifiedLoc = SS->getRange().getBegin(); + if (getLangOpts().CPlusPlus2a) + Diag(QualifiedLoc, + diag::warn_cxx17_compat_implicit_typename); + else + Diag(QualifiedLoc, diag::ext_implicit_typename) + << FixItHint::CreateInsertion(QualifiedLoc, "typename "); + } // We know from the grammar that this name refers to a type, // so build a dependent node to describe the type. if (WantNontrivialTypeSourceInfo) - return ActOnTypenameType(S, SourceLocation(), *SS, II, NameLoc).get(); + return ActOnTypenameType(S, SourceLocation(), *SS, II, NameLoc, + IsImplicitTypename) + .get(); NestedNameSpecifierLoc QualifierLoc = SS->getWithLocInContext(Context); - QualType T = CheckTypenameType(ETK_None, SourceLocation(), QualifierLoc, - II, NameLoc); + QualType T = + CheckTypenameType(IsImplicitTypename ? ETK_Typename : ETK_None, + SourceLocation(), QualifierLoc, II, NameLoc); return ParsedType::make(T); } @@ -395,7 +416,8 @@ isClassName, HasTrailingDot, ObjectTypePtr, IsCtorOrDtorName, WantNontrivialTypeSourceInfo, - IsClassTemplateDeductionContext); + IsClassTemplateDeductionContext, + AllowImplicitTypename); if (Ty) { diagnoseTypo(Correction, PDiag(diag::err_unknown_type_or_class_name_suggest) @@ -9108,11 +9130,71 @@ // marking the function. AddCFAuditedAttribute(NewFD); - // If this is a function definition, check if we have to apply optnone due to - // a pragma. - if(D.isFunctionDefinition()) + auto HasParamImplicitTypename = [this](ParmVarDecl *Param, bool Diagnostics) { + if (const auto *TSI = Param->getTypeSourceInfo()) { + if (const auto DLoc = TSI->getTypeLoc() + .getUnqualifiedLoc() + .getAs()) { + auto *DependentName = + cast(Param->getType().getTypePtr()); + if (DependentName->getKeyword() == ETK_Typename && + DLoc.getElaboratedKeywordLoc().isInvalid()) { + if (Diagnostics) + Diag(Param->getBeginLoc(), diag::err_typename_missing) + << DependentName->getQualifier() + << DependentName->getIdentifier()->getName(); + return true; + } + } + } + return false; + }; + auto DiagnoseImplicitTypename = + [this, &HasParamImplicitTypename]( + const DeclaratorChunk::FunctionTypeInfo &FuncInfo) { + for (std::size_t I = 0, Size = FuncInfo.NumParams; I < Size; ++I) { + if (HasParamImplicitTypename( + cast(FuncInfo.Params[I].Param), + /*Diagnostics=*/false)) { + SourceLocation NameLoc = FuncInfo.Params[I].Param->getBeginLoc(); + if (getLangOpts().CPlusPlus2a) + Diag(NameLoc, + diag::warn_cxx17_compat_implicit_typename); + else + Diag(NameLoc, diag::ext_implicit_typename) + << FixItHint::CreateInsertion(NameLoc, "typename "); + } + } + }; + + // If this is a function definition, check if we have to apply optnone due + // to a pragma. + if (D.isFunctionDefinition()) { AddRangeBasedOptnone(NewFD); + DeclaratorChunk::FunctionTypeInfo FuncInfo = D.getFunctionTypeInfo(); + // We delayed the diagnostic for implicit typename. Do it now. + DiagnoseImplicitTypename(FuncInfo); + } else if (D.isFunctionDeclarator()) { + // We also need to diagnose a missing 'typename' here. + // template void f(T::type, int); + // But only if 1) the function is unqualified and 2) it's at namespace + // scope. + DeclaratorChunk::FunctionTypeInfo FuncInfo = D.getFunctionTypeInfo(); + if (!D.getCXXScopeSpec().isSet() && + S->isNamespaceOrTranslationUnitScope()) { + // If it finds an implicit typename, it gets diagnosed. + std::any_of( + FuncInfo.Params, FuncInfo.Params + FuncInfo.NumParams, + [&HasParamImplicitTypename](const DeclaratorChunk::ParamInfo &PInfo) { + return PInfo.Param && + HasParamImplicitTypename(cast(PInfo.Param), + /*Diagnostics=*/true); + }); + } else if (S->isClassScope()) + DiagnoseImplicitTypename(FuncInfo); + } + // If this is the first declaration of an extern C variable, update // the map of such variables. if (NewFD->isFirstDecl() && !NewFD->isInvalidDecl() && Index: lib/Sema/SemaLookup.cpp =================================================================== --- lib/Sema/SemaLookup.cpp +++ lib/Sema/SemaLookup.cpp @@ -950,12 +950,6 @@ return Found; } -static bool isNamespaceOrTranslationUnitScope(Scope *S) { - if (DeclContext *Ctx = S->getEntity()) - return Ctx->isFileContext(); - return false; -} - // Find the next outer declaration context from this scope. This // routine actually returns the semantic outer context, which may // differ from the lexical context (encoded directly in the Scope @@ -1094,7 +1088,7 @@ // When performing a scope lookup, we want to find local extern decls. FindLocalExternScope FindLocals(R); - for (; S && !isNamespaceOrTranslationUnitScope(S); S = S->getParent()) { + for (; S && !S->isNamespaceOrTranslationUnitScope(); S = S->getParent()) { DeclContext *Ctx = S->getEntity(); bool SearchNamespaceScope = true; // Check whether the IdResolver has anything in this scope. @@ -1208,7 +1202,7 @@ // from local scopes. Scope *InnermostFileScope = S; while (InnermostFileScope && - !isNamespaceOrTranslationUnitScope(InnermostFileScope)) + !InnermostFileScope->isNamespaceOrTranslationUnitScope()) InnermostFileScope = InnermostFileScope->getParent(); UDirs.visitScopeChain(Initial, InnermostFileScope); @@ -1790,7 +1784,7 @@ // If the scope containing the declaration is the translation unit, // then we'll need to perform our checks based on the matching // DeclContexts rather than matching scopes. - if (S && isNamespaceOrTranslationUnitScope(S)) + if (S && S->isNamespaceOrTranslationUnitScope()) S = nullptr; // Compute the DeclContext, if we need it. @@ -3782,7 +3776,7 @@ UnqualUsingDirectiveSet UDirs(*this); if (getLangOpts().CPlusPlus) { // Find the first namespace or translation-unit scope. - while (S && !isNamespaceOrTranslationUnitScope(S)) + while (S && !S->isNamespaceOrTranslationUnitScope()) S = S->getParent(); UDirs.visitScopeChain(Initial, S); Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -9413,7 +9413,7 @@ TypeResult Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc, const CXXScopeSpec &SS, const IdentifierInfo &II, - SourceLocation IdLoc) { + SourceLocation IdLoc, bool IsImplicitTypename) { if (SS.isInvalid()) return true; @@ -9425,8 +9425,9 @@ << FixItHint::CreateRemoval(TypenameLoc); NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context); - QualType T = CheckTypenameType(TypenameLoc.isValid()? ETK_Typename : ETK_None, - TypenameLoc, QualifierLoc, II, IdLoc); + QualType T = CheckTypenameType( + TypenameLoc.isValid() || IsImplicitTypename ? ETK_Typename : ETK_None, + TypenameLoc, QualifierLoc, II, IdLoc); if (T.isNull()) return true; Index: test/CXX/drs/dr1xx.cpp =================================================================== --- test/CXX/drs/dr1xx.cpp +++ test/CXX/drs/dr1xx.cpp @@ -58,7 +58,7 @@ namespace dr108 { // dr108: yes template struct A { struct B { typedef int X; }; - B::X x; // expected-error {{missing 'typename'}} + B::X x; // expected-error {{implicit 'typename' is a C++2a extension}} struct C : B { X x; }; // expected-error {{unknown type name}} }; template<> struct A::B { int X; }; Index: test/CXX/drs/dr2xx.cpp =================================================================== --- test/CXX/drs/dr2xx.cpp +++ test/CXX/drs/dr2xx.cpp @@ -242,7 +242,7 @@ typedef int type; A::type a; A::type b; - A::type c; // expected-error {{missing 'typename'}} + A::type c; // expected-error {{implicit 'typename' is a C++2a extension}} ::dr224::example1::A::type d; class B { @@ -250,12 +250,12 @@ A::type a; A::type b; - A::type c; // expected-error {{missing 'typename'}} + A::type c; // expected-error {{implicit 'typename' is a C++2a extension}} ::dr224::example1::A::type d; B::type e; A::B::type f; - A::B::type g; // expected-error {{missing 'typename'}} + A::B::type g; // expected-error {{implicit 'typename' is a C++2a extension}} typename A::B::type h; }; }; @@ -263,21 +263,21 @@ template class A { typedef int type; A::type a; - A::type b; // expected-error {{missing 'typename'}} + A::type b; // expected-error {{implicit 'typename' is a C++2a extension}} }; template struct B { typedef int type; B::type b1; - B::type b2; // expected-error {{missing 'typename'}} + B::type b2; // expected-error {{implicit 'typename' is a C++2a extension}} typedef T1 my_T1; static const int my_I = I; static const int my_I2 = I+0; static const int my_I3 = my_I; - B::type b3; // FIXME: expected-error {{missing 'typename'}} - B::type b4; // expected-error {{missing 'typename'}} - B::type b5; // FIXME: expected-error {{missing 'typename'}} + B::type b3; // FIXME: expected-error {{implicit 'typename' is a C++2a extension}} + B::type b4; // expected-error {{implicit 'typename' is a C++2a extension}} + B::type b5; // FIXME: expected-error {{implicit 'typename' is a C++2a extension}} }; } @@ -285,10 +285,10 @@ template struct X { typedef T type; }; template class A { static const int i = 5; - X::type w; // FIXME: expected-error {{missing 'typename'}} - X::type x; // FIXME: expected-error {{missing 'typename'}} - X::i, double>::type y; // FIXME: expected-error {{missing 'typename'}} - X::i, long>::type z; // expected-error {{missing 'typename'}} + X::type w; // FIXME: expected-error {{implicit 'typename' is a C++2a extension}} + X::type x; // FIXME: expected-error {{implicit 'typename' is a C++2a extension}} + X::i, double>::type y; // FIXME: expected-error {{implicit 'typename' is a C++2a extension}} + X::i, long>::type z; // expected-error {{implicit 'typename' is a C++2a extension}} int f(); }; template int A::f() { Index: test/CXX/drs/dr4xx.cpp =================================================================== --- test/CXX/drs/dr4xx.cpp +++ test/CXX/drs/dr4xx.cpp @@ -173,7 +173,7 @@ B b1; A::B b2; A::B b3; - A::B b4; // expected-error {{missing 'typename'}} + A::B b4; // expected-error {{implicit 'typename' is a C++2a extension}} }; } Index: test/CXX/drs/dr5xx.cpp =================================================================== --- test/CXX/drs/dr5xx.cpp +++ test/CXX/drs/dr5xx.cpp @@ -228,8 +228,8 @@ template struct X { typedef int type; X::type v1; - X<(N)>::type v2; // expected-error {{missing 'typename'}} - X<+N>::type v3; // expected-error {{missing 'typename'}} + X<(N)>::type v2; // expected-error {{implicit 'typename' is a C++2a extension}} + X<+N>::type v3; // expected-error {{implicit 'typename' is a C++2a extension}} }; } Index: test/CXX/temp/temp.res/p5.cpp =================================================================== --- /dev/null +++ test/CXX/temp/temp.res/p5.cpp @@ -0,0 +1,141 @@ +// RUN: %clang_cc1 -std=c++2a -pedantic -verify %s + +struct X { + using type = int; + static constexpr int value = 1; + class tclass {}; +}; + +// [temp.res]p5 +// A qualified-id is assumed to name a type if + +template +void f() { + // it is a qualified name in a type-id-only context (see below), or + // [its smallest enclosing [/new/defining/]-type-id is]: + // - a new-type-id + auto *Ptr = new T::type(); + // - a defining-type-id + class T::tclass Empty1; + T::tclass Empty2; // expected-error{{missing 'typename'}} + // - a trailing-return-type + auto f()->T::type; + // - default argument of a type-parameter of a template [see below] + + // - type-id of a + // static_cast, + auto StaticCast = static_cast(1.2); + // const_cast, + const auto *ConstCast = const_cast(Ptr); + // reinterpret_cast, + int ReinterpretCast = reinterpret_cast(4); + // dynamic_cast + struct B { + virtual ~B() = default; + }; + struct D : T::tclass {}; + auto *Base = dynamic_cast(new B); + + T::type Invalid; // expected-error{{missing 'typename'}} +} + +template void f(); + +// As default argument. +template +struct DefaultArg {}; + +template struct DefaultArg; + +// it is a decl-specifier of the decl-specifier-seq of a +// - simple-declaration or a function-definition in namespace scope +template +T::type VarTemp = 1; + +template int VarTemp; + +template +T::type FuncDef() { return 1; } + +template int FuncDef(); + +template +T::type funcDecl(); + +template +void FuncParam(T::type); // ok, but variable template +// expected-error@-1{{variable has incomplete type 'void'}} + +template +void FuncParam2(const T::type, int); // expected-error{{missing 'typename'}} + +template +struct MemberDecl { + // member-declaration, + T::type Member; + + // parameter-declaration in a member-declaration, unless that + // parameter-declaration appears in a default argument + void NoDefault(T::type); + void Default(int A = T::value); +}; + +template struct MemberDecl; + +// parameter-declaration in a declarator of a function or function template +// declaration where the declarator-id is qualified, unless that +// parameter-declaration appears in a default argument, +struct QualifiedFunc { + template + void foo(typename T::type); + template + void bar(T::type); +}; + +template +void QualifiedFunc::foo(T::type) {} +template +void QualifiedFunc::bar(typename T::type) {} + +template +void g() { + // parameter-declaration in a lambda-declarator, unless that + // parameter-declaration appears in a default argument, or + auto Lambda1 = [](T::type) {}; + auto Lambda2 = [](int A = T::value) {}; +} + +template void g(); + +// parameter-declaration of a (non-type) template-parameter. +template +void NonTypeArg() {} + +template void NonTypeArg(); + +template +void f(T::type) {} + +namespace N { + template + int f(typename T::type); + template + extern int Var; +} + +template +int N::f(T::type); // ok, function +template +int N::Var(T::value); // ok, variable + +int h() { + return N::f(10) + N::Var; +} + +namespace NN { + inline namespace A { template int f(typename T::type); } // expected-note{{previous definition is here}} + inline namespace B { template int f(T::type); } +} + +template +int NN::f(T::type); // expected-error{{redefinition of 'f' as different kind of symbol}} Index: test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp =================================================================== --- test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp +++ test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp @@ -17,7 +17,7 @@ template struct A>> { struct C {}; - B>::C bc; // expected-error {{missing 'typename'}} + B>::C bc; // expected-warning {{implicit 'typename' is a C++2a extension}} }; } Index: test/FixIt/fixit.cpp =================================================================== --- test/FixIt/fixit.cpp +++ test/FixIt/fixit.cpp @@ -211,7 +211,7 @@ template struct Mystery; template typedef Mystery::type getMysteriousThing() { // \ expected-error {{function definition declared 'typedef'}} \ - expected-error {{missing 'typename' prior to dependent}} + expected-warning {{implicit 'typename' is a C++2a extension}} return Mystery::get(); } Index: test/SemaCXX/MicrosoftCompatibility.cpp =================================================================== --- test/SemaCXX/MicrosoftCompatibility.cpp +++ test/SemaCXX/MicrosoftCompatibility.cpp @@ -199,14 +199,14 @@ typedef B Base2; typedef A Base3; - A::TYPE a1; // expected-warning {{missing 'typename' prior to dependent type name}} - Base1::TYPE a2; // expected-warning {{missing 'typename' prior to dependent type name}} + A::TYPE a1; // expected-warning {{implicit 'typename' is a C++2a extension}} + Base1::TYPE a2; // expected-warning {{implicit 'typename' is a C++2a extension}} - B::TYPE a3; // expected-warning {{missing 'typename' prior to dependent type name}} - Base2::TYPE a4; // expected-warning {{missing 'typename' prior to dependent type name}} + B::TYPE a3; // expected-warning {{implicit 'typename' is a C++2a extension}} + Base2::TYPE a4; // expected-warning {{implicit 'typename' is a C++2a extension}} - A::TYPE a5; // expected-error {{missing 'typename' prior to dependent type name}} - Base3::TYPE a6; // expected-error {{missing 'typename' prior to dependent type name}} + A::TYPE a5; // expected-warning {{implicit 'typename' is a C++2a extension}} + Base3::TYPE a6; // expected-warning {{implicit 'typename' is a C++2a extension}} }; class D { @@ -215,9 +215,9 @@ }; template -void function_missing_typename(const T::Type param)// expected-warning {{missing 'typename' prior to dependent type name}} +void function_missing_typename(const T::Type param)// expected-warning {{implicit 'typename' is a C++2a extension}} { - const T::Type var = 2; // expected-warning {{missing 'typename' prior to dependent type name}} + const T::Type var = 2; // expected-warning {{missing 'typename'}} } template void function_missing_typename(const D::Type param); Index: test/SemaCXX/MicrosoftExtensions.cpp =================================================================== --- test/SemaCXX/MicrosoftExtensions.cpp +++ test/SemaCXX/MicrosoftExtensions.cpp @@ -526,7 +526,7 @@ namespace PR32750 { template struct A {}; -template struct B : A> { A::C::D d; }; // expected-error {{missing 'typename' prior to dependent type name 'A::C::D'}} +template struct B : A> { A::C::D d; }; // expected-warning {{implicit 'typename' is a C++2a extension}} } #else Index: test/SemaCXX/MicrosoftSuper.cpp =================================================================== --- test/SemaCXX/MicrosoftSuper.cpp +++ test/SemaCXX/MicrosoftSuper.cpp @@ -108,8 +108,8 @@ typename __super::XXX a; typedef typename __super::XXX b; - __super::XXX c; // expected-error {{missing 'typename'}} - typedef __super::XXX d; // expected-error {{missing 'typename'}} + __super::XXX c; // expected-warning {{implicit 'typename' is a C++2a extension}} + typedef __super::XXX d; // expected-warning {{implicit 'typename' is a C++2a extension}} void foo() { typename __super::XXX e; @@ -127,8 +127,8 @@ typename __super::XXX a; typedef typename __super::XXX b; - __super::XXX c; // expected-error {{missing 'typename'}} - typedef __super::XXX d; // expected-error {{missing 'typename'}} + __super::XXX c; // expected-warning {{implicit 'typename' is a C++2a extension}} + typedef __super::XXX d; // expected-warning {{implicit 'typename' is a C++2a extension}} void foo() { typename __super::XXX e; Index: test/SemaCXX/unknown-type-name.cpp =================================================================== --- test/SemaCXX/unknown-type-name.cpp +++ test/SemaCXX/unknown-type-name.cpp @@ -36,39 +36,39 @@ static int n; static type m; - static int h(T::type, int); // expected-error{{missing 'typename'}} - static int h(T::type x, char); // expected-error{{missing 'typename'}} + static int h(T::type, int); // expected-warning {{implicit 'typename' is a C++2a extension}} + static int h(T::type x, char); // expected-warning {{implicit 'typename' is a C++2a extension}} }; template -A::type g(T t) { return t; } // expected-error{{missing 'typename'}} +A::type g(T t) { return t; } // expected-warning {{implicit 'typename' is a C++2a extension}} template -A::type A::f() { return type(); } // expected-error{{missing 'typename'}} +A::type A::f() { return type(); } // expected-warning {{implicit 'typename' is a C++2a extension}} template -void f(T::type) { } // expected-error{{missing 'typename'}} +void f(T::type) { } // expected-warning {{implicit 'typename' is a C++2a extension}} template -void g(T::type x) { } // expected-error{{missing 'typename'}} +void g(T::type x) { } // expected-warning {{implicit 'typename' is a C++2a extension}} template -void f(T::type, int) { } // expected-error{{missing 'typename'}} +void f(T::type, int) { } // expected-warning {{implicit 'typename' is a C++2a extension}} template -void f(T::type x, char) { } // expected-error{{missing 'typename'}} +void f(T::type x, char) { } // expected-warning {{implicit 'typename' is a C++2a extension}} template -void f(int, T::type) { } // expected-error{{missing 'typename'}} +void f(int, T::type) { } // expected-warning {{implicit 'typename' is a C++2a extension}} template -void f(char, T::type x) { } // expected-error{{missing 'typename'}} +void f(char, T::type x) { } // expected-warning {{implicit 'typename' is a C++2a extension}} template -void f(int, T::type, int) { } // expected-error{{missing 'typename'}} +void f(int, T::type, int) { } // expected-warning {{implicit 'typename' is a C++2a extension}} template -void f(int, T::type x, char) { } // expected-error{{missing 'typename'}} +void f(int, T::type x, char) { } // expected-warning {{implicit 'typename' is a C++2a extension}} int *p; @@ -86,26 +86,26 @@ template int A::n(T::value); // ok template -A::type // expected-error{{missing 'typename'}} +A::type // expected-warning {{implicit 'typename' is a C++2a extension}} A::m(T::value, 0); // ok -template int A::h(T::type, int) {} // expected-error{{missing 'typename'}} -template int A::h(T::type x, char) {} // expected-error{{missing 'typename'}} +template int A::h(T::type, int) {} // expected-warning {{implicit 'typename' is a C++2a extension}} +template int A::h(T::type x, char) {} // expected-warning {{implicit 'typename' is a C++2a extension}} -template int h(T::type, int); // expected-error{{missing 'typename'}} -template int h(T::type x, char); // expected-error{{missing 'typename'}} +template int h(T::type, int); // expected-error {{missing 'typename'}} +template int h(T::type x, char); // expected-error {{missing 'typename'}} template int junk1(T::junk); #if __cplusplus <= 201103L // expected-warning@-2 {{variable templates are a C++14 extension}} #endif -template int junk2(T::junk) throw(); // expected-error{{missing 'typename'}} -template int junk3(T::junk) = delete; // expected-error{{missing 'typename'}} +template int junk2(T::junk) throw(); // expected-error {{missing 'typename'}} +template int junk3(T::junk) = delete; // expected-warning {{implicit 'typename' is a C++2a extension}} #if __cplusplus <= 199711L //expected-warning@-2 {{deleted function definitions are a C++11 extension}} #endif -template int junk4(T::junk j); // expected-error{{missing 'typename'}} +template int junk4(T::junk j); // expected-error {{missing 'typename'}} // FIXME: We can tell this was intended to be a function because it does not // have a dependent nested name specifier. @@ -118,4 +118,5 @@ // FIXME: We know which type specifier should have been specified here. Provide // a fix-it to add 'typename A::type' template -A::g() { } // expected-error{{requires a type specifier}} +A::g() { } // expected-error{{expected unqualified-id}} +// expected-warning@-1{{implicit 'typename' is a C++2a extension}}