Skip to content

Commit 27c10ea

Browse files
committed
[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
1 parent b49e6f8 commit 27c10ea

File tree

5 files changed

+112
-28
lines changed

5 files changed

+112
-28
lines changed

include/clang/Sema/Sema.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,12 +1529,13 @@ class Sema {
15291529
ParsedType &SuggestedType,
15301530
bool AllowClassTemplates = false);
15311531

1532-
/// \brief For compatibility with MSVC, we delay parsing of some default
1533-
/// template type arguments until instantiation time. Emits a warning and
1534-
/// returns a synthesized DependentNameType that isn't really dependent on any
1535-
/// other template arguments.
1536-
ParsedType ActOnDelayedDefaultTemplateArg(const IdentifierInfo &II,
1537-
SourceLocation NameLoc);
1532+
/// Attempt to behave like MSVC in situations where lookup of an unqualified
1533+
/// type name has failed in a dependent context. In these situations, we
1534+
/// automatically form a DependentTypeName that will retry lookup in a related
1535+
/// scope during instantiation.
1536+
ParsedType ActOnMSVCUnknownTypeName(const IdentifierInfo &II,
1537+
SourceLocation NameLoc,
1538+
bool IsTemplateTypeArg);
15381539

15391540
/// \brief Describes the result of the name lookup and resolution performed
15401541
/// by \c ClassifyName().

lib/Parse/ParseDecl.cpp

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2279,6 +2279,24 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
22792279
return false;
22802280
}
22812281

2282+
if (getLangOpts().CPlusPlus && (!SS || SS->isEmpty()) &&
2283+
getLangOpts().MSVCCompat) {
2284+
// Lookup of an unqualified type name has failed in MSVC compatibility mode.
2285+
// Give Sema a chance to recover if we are in a template with dependent base
2286+
// classes.
2287+
if (ParsedType T = Actions.ActOnMSVCUnknownTypeName(
2288+
*Tok.getIdentifierInfo(), Tok.getLocation(),
2289+
DSC == DSC_template_type_arg)) {
2290+
const char *PrevSpec;
2291+
unsigned DiagID;
2292+
DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID, T,
2293+
Actions.getASTContext().getPrintingPolicy());
2294+
DS.SetRangeEnd(Tok.getLocation());
2295+
ConsumeToken();
2296+
return false;
2297+
}
2298+
}
2299+
22822300
// Otherwise, if we don't consume this token, we are going to emit an
22832301
// error anyway. Try to recover from various common problems. Check
22842302
// to see if this was a reference to a tag name without a tag specified.
@@ -2986,16 +3004,6 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
29863004
Actions.getTypeName(*Tok.getIdentifierInfo(),
29873005
Tok.getLocation(), getCurScope());
29883006

2989-
// MSVC: If we weren't able to parse a default template argument, and it's
2990-
// just a simple identifier, create a DependentNameType. This will allow
2991-
// us to defer the name lookup to template instantiation time, as long we
2992-
// forge a NestedNameSpecifier for the current context.
2993-
if (!TypeRep && DSContext == DSC_template_type_arg &&
2994-
getLangOpts().MSVCCompat && getCurScope()->isTemplateParamScope()) {
2995-
TypeRep = Actions.ActOnDelayedDefaultTemplateArg(
2996-
*Tok.getIdentifierInfo(), Tok.getLocation());
2997-
}
2998-
29993007
// If this is not a typedef name, don't parse it as part of the declspec,
30003008
// it must be an implicit int or an error.
30013009
if (!TypeRep) {

lib/Sema/SemaDecl.cpp

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -473,17 +473,53 @@ synthesizeCurrentNestedNameSpecifier(ASTContext &Context, DeclContext *DC) {
473473
llvm_unreachable("something isn't in TU scope?");
474474
}
475475

476-
ParsedType Sema::ActOnDelayedDefaultTemplateArg(const IdentifierInfo &II,
477-
SourceLocation NameLoc) {
478-
// Accepting an undeclared identifier as a default argument for a template
479-
// type parameter is a Microsoft extension.
480-
Diag(NameLoc, diag::ext_ms_delayed_template_argument) << &II;
481-
482-
// Build a fake DependentNameType that will perform lookup into CurContext at
483-
// instantiation time. The name specifier isn't dependent, so template
484-
// instantiation won't transform it. It will retry the lookup, however.
485-
NestedNameSpecifier *NNS =
486-
synthesizeCurrentNestedNameSpecifier(Context, CurContext);
476+
/// Find the parent class with dependent bases of the innermost enclosing method
477+
/// context. Do not look for enclosing CXXRecordDecls directly, or we will end
478+
/// up allowing unqualified dependent type names at class-level, which MSVC
479+
/// correctly rejects.
480+
static const CXXRecordDecl *
481+
findRecordWithDependentBasesOfEnclosingMethod(const DeclContext *DC) {
482+
for (; DC && DC->isDependentContext(); DC = DC->getLookupParent()) {
483+
DC = DC->getPrimaryContext();
484+
if (const auto *MD = dyn_cast<CXXMethodDecl>(DC))
485+
if (MD->getParent()->hasAnyDependentBases())
486+
return MD->getParent();
487+
}
488+
return nullptr;
489+
}
490+
491+
ParsedType Sema::ActOnMSVCUnknownTypeName(const IdentifierInfo &II,
492+
SourceLocation NameLoc,
493+
bool IsTemplateTypeArg) {
494+
assert(getLangOpts().MSVCCompat && "shouldn't be called in non-MSVC mode");
495+
496+
NestedNameSpecifier *NNS = nullptr;
497+
if (IsTemplateTypeArg && getCurScope()->isTemplateParamScope()) {
498+
// If we weren't able to parse a default template argument, delay lookup
499+
// until instantiation time by making a non-dependent DependentTypeName. We
500+
// pretend we saw a NestedNameSpecifier referring to the current scope, and
501+
// lookup is retried.
502+
// FIXME: This hurts our diagnostic quality, since we get errors like "no
503+
// type named 'Foo' in 'current_namespace'" when the user didn't write any
504+
// name specifiers.
505+
NNS = synthesizeCurrentNestedNameSpecifier(Context, CurContext);
506+
Diag(NameLoc, diag::ext_ms_delayed_template_argument) << &II;
507+
} else if (const CXXRecordDecl *RD =
508+
findRecordWithDependentBasesOfEnclosingMethod(CurContext)) {
509+
// Build a DependentNameType that will perform lookup into RD at
510+
// instantiation time.
511+
NNS = NestedNameSpecifier::Create(Context, nullptr, RD->isTemplateDecl(),
512+
RD->getTypeForDecl());
513+
514+
// Diagnose that this identifier was undeclared, and retry the lookup during
515+
// template instantiation.
516+
Diag(NameLoc, diag::ext_undeclared_unqual_id_with_dependent_base) << &II
517+
<< RD;
518+
} else {
519+
// This is not a situation that we should recover from.
520+
return ParsedType();
521+
}
522+
487523
QualType T = Context.getDependentNameType(ETK_None, NNS, &II);
488524

489525
// Build type location information. We synthesized the qualifier, so we have

test/SemaTemplate/ms-delayed-default-template-args.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ struct Foo {
5555
typedef int Weber;
5656
}
5757

58+
// MSVC accepts this, but Clang doesn't.
59+
namespace test_scope_spec {
60+
template <typename T = ns::Bar> // expected-error {{use of undeclared identifier 'ns'}}
61+
struct Foo {
62+
static_assert(sizeof(T) == 4, "Bar should have gotten int");
63+
};
64+
namespace ns { typedef int Bar; }
65+
}
66+
5867
#ifdef __clang__
5968
// These are negative test cases that MSVC doesn't compile either. Try to use
6069
// unique undeclared identifiers so typo correction doesn't find types declared

test/SemaTemplate/ms-lookup-template-base-classes.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -std=c++1y -fms-compatibility -fno-spell-checking -fsyntax-only -verify %s
1+
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -std=c++1y -fms-compatibility -fno-spell-checking -fsyntax-only -verify %s
22

33

44
template <class T>
@@ -573,3 +573,33 @@ void h();
573573
template <typename T> decltype(h(T())) check2(); // expected-note{{candidate template ignored: substitution failure [with T = int]: no matching function for call to 'h'}}
574574
decltype(check2<int>()) y; // expected-error{{no matching function for call to 'check2'}}
575575
}
576+
577+
// We also allow unqualified lookup into bases in contexts where the we know the
578+
// undeclared identifier *must* be a type, such as a new expression or catch
579+
// parameter type.
580+
template <typename T>
581+
struct UseUnqualifiedTypeNames : T {
582+
void foo() {
583+
void *P = new TheType; // expected-warning {{unqualified lookup}} expected-error {{no type}}
584+
size_t x = __builtin_offsetof(TheType, f2); // expected-warning {{unqualified lookup}} expected-error {{no type}}
585+
try {
586+
} catch (TheType) { // expected-warning {{unqualified lookup}} expected-error {{no type}}
587+
}
588+
enum E : IntegerType { E0 = 42 }; // expected-warning {{unqualified lookup}} expected-error {{no type}}
589+
_Atomic(TheType) a; // expected-warning {{unqualified lookup}} expected-error {{no type}}
590+
}
591+
void out_of_line();
592+
};
593+
template <typename T>
594+
void UseUnqualifiedTypeNames<T>::out_of_line() {
595+
void *p = new TheType; // expected-warning {{unqualified lookup}} expected-error {{no type}}
596+
}
597+
struct Base {
598+
typedef int IntegerType;
599+
struct TheType {
600+
int f1, f2;
601+
};
602+
};
603+
template struct UseUnqualifiedTypeNames<Base>;
604+
struct BadBase { };
605+
template struct UseUnqualifiedTypeNames<BadBase>; // expected-note-re 2 {{in instantiation {{.*}} requested here}}

0 commit comments

Comments
 (0)