mirror of https://github.com/microsoft/clang.git
[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:
parent
b49e6f8c47
commit
27c10ea3c6
|
@ -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().
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}}
|
||||
|
|
Loading…
Reference in New Issue