Author: rnk Date: Fri Jun 6 17:36:36 2014 New Revision: 210382 URL: http://llvm.org/viewvc/llvm-project?rev=210382&view=rev Log: Delay lookup of simple default template arguments under -fms-compatibility
MSVC delays parsing of default arguments until instantiation. If the default argument is never used, it is never parsed. We don't model this. Instead, if lookup of a type name fails in a template argument context, we form a DependentNameType, which will be looked up at instantiation time. This fixes errors about 'CControlWinTraits' in atlwin.h. Reviewers: rsmith Differential Revision: http://reviews.llvm.org/D3995 Added: cfe/trunk/test/SemaTemplate/ms-delayed-default-template-args.cpp Modified: cfe/trunk/include/clang/AST/Type.h cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/include/clang/Parse/Parser.h cfe/trunk/include/clang/Sema/Sema.h cfe/trunk/lib/AST/ASTContext.cpp cfe/trunk/lib/Parse/ParseDecl.cpp cfe/trunk/lib/Sema/SemaDecl.cpp Modified: cfe/trunk/include/clang/AST/Type.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Type.h?rev=210382&r1=210381&r2=210382&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/Type.h (original) +++ cfe/trunk/include/clang/AST/Type.h Fri Jun 6 17:36:36 2014 @@ -4057,11 +4057,14 @@ public: /// 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 @@ class DependentNameType : public TypeWit /*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 Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=210382&r1=210381&r2=210382&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Jun 6 17:36:36 2014 @@ -3224,6 +3224,9 @@ def err_pointer_to_member_oper_value_cla def ext_ms_deref_template_argument: ExtWarn< "non-type template argument containing a dereference operation is a " "Microsoft extension">, InGroup<Microsoft>; +def ext_ms_delayed_template_argument: ExtWarn< + "using the undeclared type %0 as a default template argument is a " + "Microsoft extension">, InGroup<Microsoft>; // C++ template specialization def err_template_spec_unknown_kind : Error< Modified: cfe/trunk/include/clang/Parse/Parser.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=210382&r1=210381&r2=210382&view=diff ============================================================================== --- cfe/trunk/include/clang/Parse/Parser.h (original) +++ cfe/trunk/include/clang/Parse/Parser.h Fri Jun 6 17:36:36 2014 @@ -1691,7 +1691,8 @@ private: 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 @@ private: case DSC_top_level: return false; + case DSC_template_type_arg: case DSC_type_specifier: case DSC_trailing: case DSC_alias_declaration: Modified: cfe/trunk/include/clang/Sema/Sema.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=210382&r1=210381&r2=210382&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/Sema.h (original) +++ cfe/trunk/include/clang/Sema/Sema.h Fri Jun 6 17:36:36 2014 @@ -1418,6 +1418,13 @@ public: 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 { Modified: cfe/trunk/lib/AST/ASTContext.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=210382&r1=210381&r2=210382&view=diff ============================================================================== --- cfe/trunk/lib/AST/ASTContext.cpp (original) +++ cfe/trunk/lib/AST/ASTContext.cpp Fri Jun 6 17:36:36 2014 @@ -3318,8 +3318,6 @@ QualType ASTContext::getDependentNameTyp 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; Modified: cfe/trunk/lib/Parse/ParseDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=210382&r1=210381&r2=210382&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseDecl.cpp (original) +++ cfe/trunk/lib/Parse/ParseDecl.cpp Fri Jun 6 17:36:36 2014 @@ -2227,6 +2227,8 @@ Parser::getDeclSpecContextFromDeclarator 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 @@ void Parser::ParseDeclarationSpecifiers( 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) { Modified: cfe/trunk/lib/Sema/SemaDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=210382&r1=210381&r2=210382&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp (original) +++ cfe/trunk/lib/Sema/SemaDecl.cpp Fri Jun 6 17:36:36 2014 @@ -343,6 +343,50 @@ ParsedType Sema::getTypeName(const Ident 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<NamespaceDecl>(DC); + if (ND && !ND->isInline() && !ND->isAnonymousNamespace()) + return NestedNameSpecifier::Create(Context, nullptr, ND); + else if (auto *RD = dyn_cast<CXXRecordDecl>(DC)) + return NestedNameSpecifier::Create(Context, nullptr, RD->isTemplateDecl(), + RD->getTypeForDecl()); + else if (isa<TranslationUnitDecl>(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<DependentNameTypeLoc>(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, Added: cfe/trunk/test/SemaTemplate/ms-delayed-default-template-args.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/ms-delayed-default-template-args.cpp?rev=210382&view=auto ============================================================================== --- cfe/trunk/test/SemaTemplate/ms-delayed-default-template-args.cpp (added) +++ cfe/trunk/test/SemaTemplate/ms-delayed-default-template-args.cpp Fri Jun 6 17:36:36 2014 @@ -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 <typename T = Baz> // 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 <typename T = Baz> // 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 <typename T = Baz> // 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 <typename T> struct Bar { T x; }; +typedef int Qux; +template <Bar<Qux> *P> +struct Foo { +}; +Bar<int> g; +template struct Foo<&g>; +} + +// MSVC accepts this, but Clang doesn't. +namespace test_template_instantiation_arg { +template <typename T> struct Bar { T x; }; +template <typename T = Bar<Weber>> // 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 <Zargon N> // 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 <typename T, Asdf> // expected-error {{unknown type name 'Asdf'}} +struct Foo { T x; }; +template struct Foo<int, 0>; +} + +namespace test_undeclared_type_arg { +template <typename T> +struct Foo { T x; }; +template struct Foo<Yodel>; // 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 <typename T> struct Bar { T x; }; + +template <Bar<Xylophone> *P> // expected-error {{use of undeclared identifier 'Xylophone'}} +// expected-note@-1 {{template parameter is declared here}} +struct Foo { }; + +typedef int Xylophone; +Bar<Xylophone> g; +template struct Foo<&g>; // expected-error {{cannot be converted}} +} + +#endif _______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
