Index: cfe/trunk/include/clang/AST/TemplateBase.h =================================================================== --- cfe/trunk/include/clang/AST/TemplateBase.h +++ cfe/trunk/include/clang/AST/TemplateBase.h @@ -543,6 +543,10 @@ return Arguments[I]; } + TemplateArgumentLoc &operator[](unsigned I) { + return Arguments[I]; + } + void addArgument(const TemplateArgumentLoc &Loc) { Arguments.push_back(Loc); } Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td @@ -3111,6 +3111,10 @@ "template argument for template type parameter must be a type">; def err_template_arg_must_be_type_suggest : Error< "template argument for template type parameter must be a type; did you forget 'typename'?">; +def ext_ms_template_type_arg_missing_typename : ExtWarn< + "template argument for template type parameter must be a type; " + "omitted 'typename' is a Microsoft extension">, + InGroup; def err_template_arg_must_be_expr : Error< "template argument for non-type template parameter must be an expression">; def err_template_arg_nontype_ambig : Error< Index: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -5397,7 +5397,7 @@ }; bool CheckTemplateArgument(NamedDecl *Param, - const TemplateArgumentLoc &Arg, + TemplateArgumentLoc &Arg, NamedDecl *Template, SourceLocation TemplateLoc, SourceLocation RAngleLoc, @@ -5433,7 +5433,7 @@ SmallVectorImpl &Converted); bool CheckTemplateTypeArgument(TemplateTypeParmDecl *Param, - const TemplateArgumentLoc &Arg, + TemplateArgumentLoc &Arg, SmallVectorImpl &Converted); bool CheckTemplateArgument(TemplateTypeParmDecl *Param, @@ -5443,7 +5443,7 @@ TemplateArgument &Converted, CheckTemplateArgumentKind CTAK = CTAK_Specified); bool CheckTemplateArgument(TemplateTemplateParmDecl *Param, - const TemplateArgumentLoc &Arg, + TemplateArgumentLoc &Arg, unsigned ArgumentPackIndex); ExprResult Index: cfe/trunk/lib/Sema/SemaExpr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp +++ cfe/trunk/lib/Sema/SemaExpr.cpp @@ -2181,6 +2181,17 @@ return ExprError(); } + if (R.isSingleResult() && R.getAsSingle()) { + // Diagnose a missing typename if this resolved unambiguously to a type in a + // dependent context. + // FIXME: Issue a fixit and recover as though the user had written + // 'typename'. + Diag(SS.getBeginLoc(), diag::err_typename_missing) + << SS.getScopeRep() << NameInfo.getName().getAsString() + << SourceRange(SS.getBeginLoc(), NameInfo.getEndLoc()); + return ExprError(); + } + // Defend against this resolving to an implicit member access. We usually // won't get here if this might be a legitimate a class member (we end up in // BuildMemberReferenceExpr instead), but this can be valid if we're forming Index: cfe/trunk/lib/Sema/SemaTemplate.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaTemplate.cpp +++ cfe/trunk/lib/Sema/SemaTemplate.cpp @@ -2999,9 +2999,11 @@ } bool Sema::CheckTemplateTypeArgument(TemplateTypeParmDecl *Param, - const TemplateArgumentLoc &AL, + TemplateArgumentLoc &AL, SmallVectorImpl &Converted) { const TemplateArgument &Arg = AL.getArgument(); + QualType ArgType; + TypeSourceInfo *TSI = nullptr; // Check template type parameter. switch(Arg.getKind()) { @@ -3009,6 +3011,8 @@ // C++ [temp.arg.type]p1: // A template-argument for a template-parameter which is a // type shall be a type-id. + ArgType = Arg.getAsType(); + TSI = AL.getTypeSourceInfo(); break; case TemplateArgument::Template: { // We have a template type parameter but the template argument @@ -3043,18 +3047,38 @@ } } - if (NameInfo.getName().isIdentifier()) { + if (auto *II = NameInfo.getName().getAsIdentifierInfo()) { LookupResult Result(*this, NameInfo, LookupOrdinaryName); LookupParsedName(Result, CurScope, &SS); if (Result.getAsSingle() || Result.getResultKind() == - LookupResult::NotFoundInCurrentInstantiation) { - // FIXME: Add a FixIt and fix up the template argument for recovery. + LookupResult::NotFoundInCurrentInstantiation) { + // Suggest that the user add 'typename' before the NNS. SourceLocation Loc = AL.getSourceRange().getBegin(); - Diag(Loc, diag::err_template_arg_must_be_type_suggest); + Diag(Loc, getLangOpts().MSVCCompat + ? diag::ext_ms_template_type_arg_missing_typename + : diag::err_template_arg_must_be_type_suggest) + << FixItHint::CreateInsertion(Loc, "typename "); Diag(Param->getLocation(), diag::note_template_param_here); - return true; + + // Recover by synthesizing a type using the location information that we + // already have. + ArgType = + Context.getDependentNameType(ETK_Typename, SS.getScopeRep(), II); + TypeLocBuilder TLB; + DependentNameTypeLoc TL = TLB.push(ArgType); + TL.setElaboratedKeywordLoc(SourceLocation(/*synthesized*/)); + TL.setQualifierLoc(SS.getWithLocInContext(Context)); + TL.setNameLoc(NameInfo.getLoc()); + TSI = TLB.getTypeSourceInfo(Context, ArgType); + + // Overwrite our input TemplateArgumentLoc so that we can recover + // properly. + AL = TemplateArgumentLoc(TemplateArgument(ArgType), + TemplateArgumentLocInfo(TSI)); + + break; } } // fallthrough @@ -3070,11 +3094,11 @@ } } - if (CheckTemplateArgument(Param, AL.getTypeSourceInfo())) + if (CheckTemplateArgument(Param, TSI)) return true; // Add the converted template type argument. - QualType ArgType = Context.getCanonicalType(Arg.getAsType()); + ArgType = Context.getCanonicalType(ArgType); // Objective-C ARC: // If an explicitly-specified template argument type is a lifetime type @@ -3356,7 +3380,7 @@ /// /// \returns true on error, false otherwise. bool Sema::CheckTemplateArgument(NamedDecl *Param, - const TemplateArgumentLoc &Arg, + TemplateArgumentLoc &Arg, NamedDecl *Template, SourceLocation TemplateLoc, SourceLocation RAngleLoc, @@ -5078,7 +5102,7 @@ /// This routine implements the semantics of C++ [temp.arg.template]. /// It returns true if an error occurred, and false otherwise. bool Sema::CheckTemplateArgument(TemplateTemplateParmDecl *Param, - const TemplateArgumentLoc &Arg, + TemplateArgumentLoc &Arg, unsigned ArgumentPackIndex) { TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern(); TemplateDecl *Template = Name.getAsTemplateDecl(); Index: cfe/trunk/test/SemaTemplate/typename-specifier.cpp =================================================================== --- cfe/trunk/test/SemaTemplate/typename-specifier.cpp +++ cfe/trunk/test/SemaTemplate/typename-specifier.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify %s -Wno-unused +// RUN: %clang_cc1 -fsyntax-only -verify %s -Wno-unused -fms-compatibility -DMSVC namespace N { struct A { typedef int type; @@ -136,19 +137,106 @@ }; void foo() { - pair i; // expected-error {{template argument for template type parameter must be a type; did you forget 'typename'?}} +#ifdef MSVC + // expected-warning@+4 {{omitted 'typename' is a Microsoft extension}} +#else + // expected-error@+2 {{template argument for template type parameter must be a type; did you forget 'typename'?}} +#endif + pair i; pairExampleItemSet::iterator, int> i; // expected-error-re {{template argument for template type parameter must be a type{{$}}}} pair i; // expected-error-re {{template argument for template type parameter must be a type{{$}}}} } - pair elt; // expected-error {{template argument for template type parameter must be a type; did you forget 'typename'?}} +#ifdef MSVC + // expected-warning@+4 {{omitted 'typename' is a Microsoft extension}} +#else + // expected-error@+2 {{template argument for template type parameter must be a type; did you forget 'typename'?}} +#endif + pair elt; typedef map ExampleItemMap; static void bar() { - pair i; // expected-error {{template argument for template type parameter must be a type; did you forget 'typename'?}} +#ifdef MSVC + // expected-warning@+4 {{omitted 'typename' is a Microsoft extension}} +#else + // expected-error@+2 {{template argument for template type parameter must be a type; did you forget 'typename'?}} +#endif + pair i; } - pair entry; // expected-error {{template argument for template type parameter must be a type; did you forget 'typename'?}} +#ifdef MSVC + // expected-warning@+4 {{omitted 'typename' is a Microsoft extension}} +#else + // expected-error@+2 {{template argument for template type parameter must be a type; did you forget 'typename'?}} +#endif + pair entry; pair foobar; // expected-error {{template argument for template type parameter must be a type}} }; } // namespace missing_typename + +namespace missing_typename_and_base { +template struct Bar {}; // expected-note 1+ {{template parameter is declared here}} +template +struct Foo : T { + + // FIXME: MSVC accepts this code. + Bar x; // expected-error {{use of undeclared identifier 'TypeInBase'}} + +#ifdef MSVC + // expected-warning@+4 {{omitted 'typename' is a Microsoft extension}} +#else + // expected-error@+2 {{must be a type; did you forget 'typename'?}} +#endif + Bar y; + +#ifdef MSVC + // expected-warning@+4 {{omitted 'typename' is a Microsoft extension}} +#else + // expected-error@+2 {{must be a type; did you forget 'typename'?}} +#endif + Bar z; + +}; +struct Base { + typedef int TypeInBase; + struct NestedRD { + typedef int TypeInNestedRD; + }; +}; +Foo x; +} // namespace missing_typename_and_base + +namespace func_type_vs_construct_tmp { +template struct S { typedef int type; }; +template void f(); +template void f(); + +// expected-error@+1 {{missing 'typename' prior to dependent type name 'S::type'}} +template void g() { f::type(int())>(); } + +// Adding typename does fix the diagnostic. +template void h() { f::type(int())>(); } + +void j() { + g(); // expected-note-re {{in instantiation {{.*}} requested here}} + h(); +} +} // namespace func_type_vs_construct_tmp + +namespace pointer_vs_multiply { +int x; +// expected-error@+1 {{missing 'typename' prior to dependent type name 'B::type_or_int'}} +template void g() { T::type_or_int * x; } +// expected-error@+1 {{typename specifier refers to non-type member 'type_or_int' in 'pointer_vs_multiply::A'}} +template void h() { typename T::type_or_int * x; } + +struct A { static const int type_or_int = 5; }; // expected-note {{referenced member 'type_or_int' is declared here}} +struct B { typedef int type_or_int; }; + +void j() { + g(); + g(); // expected-note-re {{in instantiation {{.*}} requested here}} + h(); // expected-note-re {{in instantiation {{.*}} requested here}} + h(); +} +} // namespace pointer_vs_multiply