Index: cfe/trunk/include/clang/AST/Type.h =================================================================== --- cfe/trunk/include/clang/AST/Type.h +++ cfe/trunk/include/clang/AST/Type.h @@ -4057,11 +4057,14 @@ /// dependent. /// /// DependentNameType represents a class of dependent types that involve a -/// dependent nested-name-specifier (e.g., "T::") followed by a (dependent) +/// possibly dependent nested-name-specifier (e.g., "T::") followed by a /// name of a type. The DependentNameType may start with a "typename" (for a /// typename-specifier), "class", "struct", "union", or "enum" (for a /// dependent elaborated-type-specifier), or nothing (in contexts where we /// know that we must be referring to a type, e.g., in a base class specifier). +/// Typically the nested-name-specifier is dependent, but in MSVC compatibility +/// mode, this type is used with non-dependent names to delay name lookup until +/// instantiation. class DependentNameType : public TypeWithKeyword, public llvm::FoldingSetNode { /// \brief The nested name specifier containing the qualifier. @@ -4076,10 +4079,7 @@ /*InstantiationDependent=*/true, /*VariablyModified=*/false, NNS->containsUnexpandedParameterPack()), - NNS(NNS), Name(Name) { - assert(NNS->isDependent() && - "DependentNameType requires a dependent nested-name-specifier"); - } + NNS(NNS), Name(Name) {} friend class ASTContext; // ASTContext creates these Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td @@ -3224,6 +3224,9 @@ def ext_ms_deref_template_argument: ExtWarn< "non-type template argument containing a dereference operation is a " "Microsoft extension">, InGroup; +def ext_ms_delayed_template_argument: ExtWarn< + "using the undeclared type %0 as a default template argument is a " + "Microsoft extension">, InGroup; // C++ template specialization def err_template_spec_unknown_kind : Error< Index: cfe/trunk/include/clang/Parse/Parser.h =================================================================== --- cfe/trunk/include/clang/Parse/Parser.h +++ cfe/trunk/include/clang/Parse/Parser.h @@ -1691,7 +1691,8 @@ DSC_type_specifier, // C++ type-specifier-seq or C specifier-qualifier-list DSC_trailing, // C++11 trailing-type-specifier in a trailing return type DSC_alias_declaration, // C++11 type-specifier-seq in an alias-declaration - DSC_top_level // top-level/namespace declaration context + DSC_top_level, // top-level/namespace declaration context + DSC_template_type_arg // template type argument context }; /// Is this a context in which we are parsing just a type-specifier (or @@ -1703,6 +1704,7 @@ case DSC_top_level: return false; + case DSC_template_type_arg: case DSC_type_specifier: case DSC_trailing: case DSC_alias_declaration: Index: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -1418,6 +1418,13 @@ ParsedType &SuggestedType, bool AllowClassTemplates = false); + /// \brief For compatibility with MSVC, we delay parsing of some default + /// template type arguments until instantiation time. Emits a warning and + /// returns a synthesized DependentNameType that isn't really dependent on any + /// other template arguments. + ParsedType ActOnDelayedDefaultTemplateArg(const IdentifierInfo &II, + SourceLocation NameLoc); + /// \brief Describes the result of the name lookup and resolution performed /// by \c ClassifyName(). enum NameClassificationKind { Index: cfe/trunk/lib/AST/ASTContext.cpp =================================================================== --- cfe/trunk/lib/AST/ASTContext.cpp +++ cfe/trunk/lib/AST/ASTContext.cpp @@ -3318,8 +3318,6 @@ NestedNameSpecifier *NNS, const IdentifierInfo *Name, QualType Canon) const { - assert(NNS->isDependent() && "nested-name-specifier must be dependent"); - if (Canon.isNull()) { NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS); ElaboratedTypeKeyword CanonKeyword = Keyword; Index: cfe/trunk/lib/Parse/ParseDecl.cpp =================================================================== --- cfe/trunk/lib/Parse/ParseDecl.cpp +++ cfe/trunk/lib/Parse/ParseDecl.cpp @@ -2227,6 +2227,8 @@ return DSC_class; if (Context == Declarator::FileContext) return DSC_top_level; + if (Context == Declarator::TemplateTypeArgContext) + return DSC_template_type_arg; if (Context == Declarator::TrailingReturnContext) return DSC_trailing; if (Context == Declarator::AliasDeclContext || @@ -2753,6 +2755,16 @@ Actions.getTypeName(*Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope()); + // MSVC: If we weren't able to parse a default template argument, and it's + // just a simple identifier, create a DependentNameType. This will allow us + // to defer the name lookup to template instantiation time, as long we forge a + // NestedNameSpecifier for the current context. + if (!TypeRep && DSContext == DSC_template_type_arg && + getLangOpts().MSVCCompat && getCurScope()->isTemplateParamScope()) { + TypeRep = Actions.ActOnDelayedDefaultTemplateArg( + *Tok.getIdentifierInfo(), Tok.getLocation()); + } + // If this is not a typedef name, don't parse it as part of the declspec, // it must be an implicit int or an error. if (!TypeRep) { Index: cfe/trunk/lib/Sema/SemaDecl.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp +++ cfe/trunk/lib/Sema/SemaDecl.cpp @@ -343,6 +343,50 @@ return ParsedType::make(T); } +// Builds a fake NNS for the given decl context. +static NestedNameSpecifier * +synthesizeCurrentNestedNameSpecifier(ASTContext &Context, DeclContext *DC) { + for (;; DC = DC->getLookupParent()) { + DC = DC->getPrimaryContext(); + auto *ND = dyn_cast(DC); + if (ND && !ND->isInline() && !ND->isAnonymousNamespace()) + return NestedNameSpecifier::Create(Context, nullptr, ND); + else if (auto *RD = dyn_cast(DC)) + return NestedNameSpecifier::Create(Context, nullptr, RD->isTemplateDecl(), + RD->getTypeForDecl()); + else if (isa(DC)) + return NestedNameSpecifier::GlobalSpecifier(Context); + } + llvm_unreachable("something isn't in TU scope?"); +} + +ParsedType Sema::ActOnDelayedDefaultTemplateArg(const IdentifierInfo &II, + SourceLocation NameLoc) { + // Accepting an undeclared identifier as a default argument for a template + // type parameter is a Microsoft extension. + Diag(NameLoc, diag::ext_ms_delayed_template_argument) << &II; + + // Build a fake DependentNameType that will perform lookup into CurContext at + // instantiation time. The name specifier isn't dependent, so template + // instantiation won't transform it. It will retry the lookup, however. + NestedNameSpecifier *NNS = + synthesizeCurrentNestedNameSpecifier(Context, CurContext); + QualType T = Context.getDependentNameType(ETK_None, NNS, &II); + + // Build type location information. We synthesized the qualifier, so we have + // to build a fake NestedNameSpecifierLoc. + NestedNameSpecifierLocBuilder NNSLocBuilder; + NNSLocBuilder.MakeTrivial(Context, NNS, SourceRange(NameLoc)); + NestedNameSpecifierLoc QualifierLoc = NNSLocBuilder.getWithLocInContext(Context); + + TypeLocBuilder Builder; + DependentNameTypeLoc DepTL = Builder.push(T); + DepTL.setNameLoc(NameLoc); + DepTL.setElaboratedKeywordLoc(SourceLocation()); + DepTL.setQualifierLoc(QualifierLoc); + return CreateParsedType(T, Builder.getTypeSourceInfo(Context, T)); +} + /// isTagName() - This method is called *for error recovery purposes only* /// to determine if the specified name is a valid tag name ("struct foo"). If /// so, this returns the TST for the tag corresponding to it (TST_enum, Index: cfe/trunk/test/SemaTemplate/ms-delayed-default-template-args.cpp =================================================================== --- cfe/trunk/test/SemaTemplate/ms-delayed-default-template-args.cpp +++ cfe/trunk/test/SemaTemplate/ms-delayed-default-template-args.cpp @@ -0,0 +1,96 @@ +// RUN: %clang_cc1 -fms-compatibility -std=c++11 %s -verify + +// MSVC should compile this file without errors. + +namespace test_basic { +template // expected-warning {{using the undeclared type 'Baz' as a default template argument is a Microsoft extension}} +struct Foo { T x; }; +typedef int Baz; +template struct Foo<>; +} + +namespace test_namespace { +namespace nested { +template // expected-warning {{using the undeclared type 'Baz' as a default template argument is a Microsoft extension}} +struct Foo { + static_assert(sizeof(T) == 4, "should get int, not double"); +}; +typedef int Baz; +} +typedef double Baz; +template struct nested::Foo<>; +} + +namespace test_inner_class_template { +struct Outer { + template // expected-warning {{using the undeclared type 'Baz' as a default template argument is a Microsoft extension}} + struct Foo { + static_assert(sizeof(T) == 4, "should get int, not double"); + }; + typedef int Baz; +}; +typedef double Baz; +template struct Outer::Foo<>; +} + +namespace test_nontype_param { +template struct Bar { T x; }; +typedef int Qux; +template *P> +struct Foo { +}; +Bar g; +template struct Foo<&g>; +} + +// MSVC accepts this, but Clang doesn't. +namespace test_template_instantiation_arg { +template struct Bar { T x; }; +template > // expected-error {{use of undeclared identifier 'Weber'}} +struct Foo { + static_assert(sizeof(T) == 4, "Bar should have gotten int"); + // FIXME: These diagnostics are bad. +}; // expected-error {{expected ',' or '>' in template-parameter-list}} +// expected-warning@-1 {{does not declare anything}} +typedef int Weber; +} + +#ifdef __clang__ +// These are negative test cases that MSVC doesn't compile either. Try to use +// unique undeclared identifiers so typo correction doesn't find types declared +// above. + +namespace test_undeclared_nontype_parm_type { +template // expected-error {{unknown type name 'Zargon'}} +struct Foo { int x[N]; }; +typedef int Zargon; +template struct Foo<4>; +} + +namespace test_undeclared_nontype_parm_type_no_name { +template // expected-error {{unknown type name 'Asdf'}} +struct Foo { T x; }; +template struct Foo; +} + +namespace test_undeclared_type_arg { +template +struct Foo { T x; }; +template struct Foo; // expected-error {{use of undeclared identifier 'Yodel'}} +} + +namespace test_undeclared_nontype_parm_arg { +// Bury an undeclared type as a template argument to the type of a non-type +// template parameter. +template struct Bar { T x; }; + +template *P> // expected-error {{use of undeclared identifier 'Xylophone'}} +// expected-note@-1 {{template parameter is declared here}} +struct Foo { }; + +typedef int Xylophone; +Bar g; +template struct Foo<&g>; // expected-error {{cannot be converted}} +} + +#endif