[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
This commit is contained in:
Reid Kleckner 2016-05-24 21:23:54 +00:00
parent b49e6f8c47
commit 27c10ea3c6
5 changed files with 111 additions and 27 deletions

View File

@ -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().

View File

@ -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) {

View File

@ -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;
/// 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();
}
// 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

View File

@ -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

View File

@ -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}}