summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReid Kleckner <rnk@google.com>2016-05-24 21:23:54 +0000
committerReid Kleckner <rnk@google.com>2016-05-24 21:23:54 +0000
commit27c10ea3c6816d78f62152fb0ddd0516d25afe88 (patch)
treee264757f1ae11959b247a4e7efcae9f3b0243b2b
parentb49e6f8c472760ae8c44318ea7e04b99a7a0a75e (diff)
downloadclang-27c10ea3c6816d78f62152fb0ddd0516d25afe88.tar.gz
[ms] Allow more unqualified lookup of types in dependent base classes
Summary: In dependent contexts where we know a type name is required, such as a new expression, we can recover by forming a DependentNameType. This generalizes our existing compatibility hack for default arguments for template type parameters. Works towards parsing atlctrlw.h, which is PR26748. Reviewers: avt77, rsmith Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D20500 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@270615 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Sema/Sema.h13
-rw-r--r--lib/Parse/ParseDecl.cpp28
-rw-r--r--lib/Sema/SemaDecl.cpp58
-rw-r--r--test/SemaTemplate/ms-delayed-default-template-args.cpp9
-rw-r--r--test/SemaTemplate/ms-lookup-template-base-classes.cpp32
5 files changed, 112 insertions, 28 deletions
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index c2b7cabced..e09576e670 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -1529,12 +1529,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);
+ /// Attempt to behave like MSVC in situations where lookup of an unqualified
+ /// type name has failed in a dependent context. In these situations, we
+ /// automatically form a DependentTypeName that will retry lookup in a related
+ /// scope during instantiation.
+ ParsedType ActOnMSVCUnknownTypeName(const IdentifierInfo &II,
+ SourceLocation NameLoc,
+ bool IsTemplateTypeArg);
/// \brief Describes the result of the name lookup and resolution performed
/// by \c ClassifyName().
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 8a3110c515..746f996d36 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -2279,6 +2279,24 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
return false;
}
+ if (getLangOpts().CPlusPlus && (!SS || SS->isEmpty()) &&
+ getLangOpts().MSVCCompat) {
+ // Lookup of an unqualified type name has failed in MSVC compatibility mode.
+ // Give Sema a chance to recover if we are in a template with dependent base
+ // classes.
+ if (ParsedType T = Actions.ActOnMSVCUnknownTypeName(
+ *Tok.getIdentifierInfo(), Tok.getLocation(),
+ DSC == DSC_template_type_arg)) {
+ const char *PrevSpec;
+ unsigned DiagID;
+ DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID, T,
+ Actions.getASTContext().getPrintingPolicy());
+ DS.SetRangeEnd(Tok.getLocation());
+ ConsumeToken();
+ return false;
+ }
+ }
+
// Otherwise, if we don't consume this token, we are going to emit an
// error anyway. Try to recover from various common problems. Check
// to see if this was a reference to a tag name without a tag specified.
@@ -2986,16 +3004,6 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
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) {
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 81460418ca..460946d0ba 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -473,17 +473,53 @@ synthesizeCurrentNestedNameSpecifier(ASTContext &Context, DeclContext *DC) {
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);
+/// Find the parent class with dependent bases of the innermost enclosing method
+/// context. Do not look for enclosing CXXRecordDecls directly, or we will end
+/// up allowing unqualified dependent type names at class-level, which MSVC
+/// correctly rejects.
+static const CXXRecordDecl *
+findRecordWithDependentBasesOfEnclosingMethod(const DeclContext *DC) {
+ for (; DC && DC->isDependentContext(); DC = DC->getLookupParent()) {
+ DC = DC->getPrimaryContext();
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(DC))
+ if (MD->getParent()->hasAnyDependentBases())
+ return MD->getParent();
+ }
+ return nullptr;
+}
+
+ParsedType Sema::ActOnMSVCUnknownTypeName(const IdentifierInfo &II,
+ SourceLocation NameLoc,
+ bool IsTemplateTypeArg) {
+ assert(getLangOpts().MSVCCompat && "shouldn't be called in non-MSVC mode");
+
+ NestedNameSpecifier *NNS = nullptr;
+ if (IsTemplateTypeArg && getCurScope()->isTemplateParamScope()) {
+ // If we weren't able to parse a default template argument, delay lookup
+ // until instantiation time by making a non-dependent DependentTypeName. We
+ // pretend we saw a NestedNameSpecifier referring to the current scope, and
+ // lookup is retried.
+ // FIXME: This hurts our diagnostic quality, since we get errors like "no
+ // type named 'Foo' in 'current_namespace'" when the user didn't write any
+ // name specifiers.
+ NNS = synthesizeCurrentNestedNameSpecifier(Context, CurContext);
+ Diag(NameLoc, diag::ext_ms_delayed_template_argument) << &II;
+ } else if (const CXXRecordDecl *RD =
+ findRecordWithDependentBasesOfEnclosingMethod(CurContext)) {
+ // Build a DependentNameType that will perform lookup into RD at
+ // instantiation time.
+ NNS = NestedNameSpecifier::Create(Context, nullptr, RD->isTemplateDecl(),
+ RD->getTypeForDecl());
+
+ // Diagnose that this identifier was undeclared, and retry the lookup during
+ // template instantiation.
+ Diag(NameLoc, diag::ext_undeclared_unqual_id_with_dependent_base) << &II
+ << RD;
+ } else {
+ // This is not a situation that we should recover from.
+ return ParsedType();
+ }
+
QualType T = Context.getDependentNameType(ETK_None, NNS, &II);
// Build type location information. We synthesized the qualifier, so we have
diff --git a/test/SemaTemplate/ms-delayed-default-template-args.cpp b/test/SemaTemplate/ms-delayed-default-template-args.cpp
index ca9ddb0d9d..0c05469424 100644
--- a/test/SemaTemplate/ms-delayed-default-template-args.cpp
+++ b/test/SemaTemplate/ms-delayed-default-template-args.cpp
@@ -55,6 +55,15 @@ struct Foo {
typedef int Weber;
}
+// MSVC accepts this, but Clang doesn't.
+namespace test_scope_spec {
+template <typename T = ns::Bar> // expected-error {{use of undeclared identifier 'ns'}}
+struct Foo {
+ static_assert(sizeof(T) == 4, "Bar should have gotten int");
+};
+namespace ns { typedef int Bar; }
+}
+
#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
diff --git a/test/SemaTemplate/ms-lookup-template-base-classes.cpp b/test/SemaTemplate/ms-lookup-template-base-classes.cpp
index 4f3df277d9..51d19cef38 100644
--- a/test/SemaTemplate/ms-lookup-template-base-classes.cpp
+++ b/test/SemaTemplate/ms-lookup-template-base-classes.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++1y -fms-compatibility -fno-spell-checking -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -std=c++1y -fms-compatibility -fno-spell-checking -fsyntax-only -verify %s
template <class T>
@@ -573,3 +573,33 @@ void h();
template <typename T> decltype(h(T())) check2(); // expected-note{{candidate template ignored: substitution failure [with T = int]: no matching function for call to 'h'}}
decltype(check2<int>()) y; // expected-error{{no matching function for call to 'check2'}}
}
+
+// We also allow unqualified lookup into bases in contexts where the we know the
+// undeclared identifier *must* be a type, such as a new expression or catch
+// parameter type.
+template <typename T>
+struct UseUnqualifiedTypeNames : T {
+ void foo() {
+ void *P = new TheType; // expected-warning {{unqualified lookup}} expected-error {{no type}}
+ size_t x = __builtin_offsetof(TheType, f2); // expected-warning {{unqualified lookup}} expected-error {{no type}}
+ try {
+ } catch (TheType) { // expected-warning {{unqualified lookup}} expected-error {{no type}}
+ }
+ enum E : IntegerType { E0 = 42 }; // expected-warning {{unqualified lookup}} expected-error {{no type}}
+ _Atomic(TheType) a; // expected-warning {{unqualified lookup}} expected-error {{no type}}
+ }
+ void out_of_line();
+};
+template <typename T>
+void UseUnqualifiedTypeNames<T>::out_of_line() {
+ void *p = new TheType; // expected-warning {{unqualified lookup}} expected-error {{no type}}
+}
+struct Base {
+ typedef int IntegerType;
+ struct TheType {
+ int f1, f2;
+ };
+};
+template struct UseUnqualifiedTypeNames<Base>;
+struct BadBase { };
+template struct UseUnqualifiedTypeNames<BadBase>; // expected-note-re 2 {{in instantiation {{.*}} requested here}}