Rework Microsoft __if_exists/__if_not_exists parsing and semantic
analysis to separate dependent names from non-dependent names. For
dependent names, we'll behave differently from Visual C++:
- For __if_exists/__if_not_exists at class scope, we'll just warn
and then ignore them.
- For __if_exists/__if_not_exists in statements, we'll treat the
inner statement as a compound statement, which we only instantiate
in templates where the dependent name (after instantiation)
exists. This behavior is different from VC++, but it's as close as
we can get without encroaching ridiculousness.
The latter part (dependent statements) is not yet implemented.
llvm-svn: 142864
This commit is contained in:
@@ -75,6 +75,10 @@ def warn_cxx98_compat_alignof : Warning<
|
||||
"alignof expressions are incompatible with C++98">,
|
||||
InGroup<CXX98Compat>, DefaultIgnore;
|
||||
|
||||
def warn_microsoft_dependent_exists : Warning<
|
||||
"dependent %select{__if_not_exists|__if_exists}0 declarations are ignored">,
|
||||
InGroup<DiagGroup<"microsoft-exists">>;
|
||||
|
||||
def ext_c1x_generic_selection : Extension<
|
||||
"generic selections are a C1X-specific feature">, InGroup<C1X>;
|
||||
def err_duplicate_default_assoc : Error<
|
||||
|
||||
@@ -488,6 +488,7 @@ private:
|
||||
const char *Msg = "",
|
||||
tok::TokenKind SkipToTok = tok::unknown);
|
||||
bool consumeClose();
|
||||
void skipToEnd();
|
||||
};
|
||||
|
||||
DelimiterTracker QuantityTracker;
|
||||
@@ -1487,7 +1488,40 @@ private:
|
||||
StmtResult ParseReturnStatement(ParsedAttributes &Attr);
|
||||
StmtResult ParseAsmStatement(bool &msAsm);
|
||||
StmtResult ParseMicrosoftAsmStatement(SourceLocation AsmLoc);
|
||||
bool ParseMicrosoftIfExistsCondition(bool& Result);
|
||||
|
||||
/// \brief Describes the behavior that should be taken for an __if_exists
|
||||
/// block.
|
||||
enum IfExistsBehavior {
|
||||
/// \brief Parse the block; this code is always used.
|
||||
IEB_Parse,
|
||||
/// \brief Skip the block entirely; this code is never used.
|
||||
IEB_Skip,
|
||||
/// \brief Parse the block as a dependent block, which may be used in
|
||||
/// some template instantiations but not others.
|
||||
IEB_Dependent
|
||||
};
|
||||
|
||||
/// \brief Describes the condition of a Microsoft __if_exists or
|
||||
/// __if_not_exists block.
|
||||
struct IfExistsCondition {
|
||||
/// \brief The location of the initial keyword.
|
||||
SourceLocation KeywordLoc;
|
||||
/// \brief Whether this is an __if_exists block (rather than an
|
||||
/// __if_not_exists block).
|
||||
bool IsIfExists;
|
||||
|
||||
/// \brief Nested-name-specifier preceding the name.
|
||||
CXXScopeSpec SS;
|
||||
|
||||
/// \brief The name we're looking for.
|
||||
UnqualifiedId Name;
|
||||
|
||||
/// \brief The behavior of this __if_exists or __if_not_exists block
|
||||
/// should.
|
||||
IfExistsBehavior Behavior;
|
||||
};
|
||||
|
||||
bool ParseMicrosoftIfExistsCondition(IfExistsCondition& Result);
|
||||
void ParseMicrosoftIfExistsStatement(StmtVector &Stmts);
|
||||
void ParseMicrosoftIfExistsExternalDeclaration();
|
||||
void ParseMicrosoftIfExistsClassDeclaration(DeclSpec::TST TagType,
|
||||
|
||||
@@ -2591,7 +2591,20 @@ public:
|
||||
|
||||
bool CheckCaseExpression(Expr *E);
|
||||
|
||||
bool CheckMicrosoftIfExistsSymbol(CXXScopeSpec &SS, UnqualifiedId &Name);
|
||||
/// \brief Describes the result of an "if-exists" condition check.
|
||||
enum IfExistsResult {
|
||||
/// \brief The symbol exists.
|
||||
IER_Exists,
|
||||
|
||||
/// \brief The symbol does not exist.
|
||||
IER_DoesNotExist,
|
||||
|
||||
/// \brief The name is a dependent name, so it
|
||||
IER_Dependent
|
||||
};
|
||||
|
||||
IfExistsResult
|
||||
CheckMicrosoftIfExistsSymbol(Scope *S, CXXScopeSpec &SS, UnqualifiedId &Name);
|
||||
|
||||
//===------------------------- "Block" Extension ------------------------===//
|
||||
|
||||
|
||||
@@ -2757,25 +2757,32 @@ void Parser::ParseMicrosoftAttributes(ParsedAttributes &attrs,
|
||||
|
||||
void Parser::ParseMicrosoftIfExistsClassDeclaration(DeclSpec::TST TagType,
|
||||
AccessSpecifier& CurAS) {
|
||||
bool Result;
|
||||
IfExistsCondition Result;
|
||||
if (ParseMicrosoftIfExistsCondition(Result))
|
||||
return;
|
||||
|
||||
if (Tok.isNot(tok::l_brace)) {
|
||||
BalancedDelimiterTracker Braces(*this, tok::l_brace);
|
||||
if (Braces.consumeOpen()) {
|
||||
Diag(Tok, diag::err_expected_lbrace);
|
||||
return;
|
||||
}
|
||||
ConsumeBrace();
|
||||
|
||||
// Condition is false skip all inside the {}.
|
||||
if (!Result) {
|
||||
SkipUntil(tok::r_brace, false);
|
||||
switch (Result.Behavior) {
|
||||
case IEB_Parse:
|
||||
// Parse the declarations below.
|
||||
break;
|
||||
|
||||
case IEB_Dependent:
|
||||
Diag(Result.KeywordLoc, diag::warn_microsoft_dependent_exists)
|
||||
<< Result.IsIfExists;
|
||||
// Fall through to skip.
|
||||
|
||||
case IEB_Skip:
|
||||
Braces.skipToEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
// Condition is true, parse the declaration.
|
||||
while (Tok.isNot(tok::r_brace)) {
|
||||
|
||||
while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
|
||||
// __if_exists, __if_not_exists can nest.
|
||||
if ((Tok.is(tok::kw___if_exists) || Tok.is(tok::kw___if_not_exists))) {
|
||||
ParseMicrosoftIfExistsClassDeclaration((DeclSpec::TST)TagType, CurAS);
|
||||
@@ -2809,9 +2816,5 @@ void Parser::ParseMicrosoftIfExistsClassDeclaration(DeclSpec::TST TagType,
|
||||
ParseCXXClassMemberDeclaration(CurAS, 0);
|
||||
}
|
||||
|
||||
if (Tok.isNot(tok::r_brace)) {
|
||||
Diag(Tok, diag::err_expected_rbrace);
|
||||
return;
|
||||
}
|
||||
ConsumeBrace();
|
||||
Braces.consumeClose();
|
||||
}
|
||||
|
||||
@@ -2136,19 +2136,43 @@ StmtResult Parser::ParseCXXCatchBlock() {
|
||||
}
|
||||
|
||||
void Parser::ParseMicrosoftIfExistsStatement(StmtVector &Stmts) {
|
||||
bool Result;
|
||||
IfExistsCondition Result;
|
||||
if (ParseMicrosoftIfExistsCondition(Result))
|
||||
return;
|
||||
|
||||
if (Tok.isNot(tok::l_brace)) {
|
||||
// Handle dependent statements by parsing the braces as a compound statement.
|
||||
// This is not the same behavior as Visual C++, which don't treat this as a
|
||||
// compound statement, but for Clang's type checking we can't have anything
|
||||
// inside these braces escaping to the surrounding code.
|
||||
if (Result.Behavior == IEB_Dependent) {
|
||||
if (!Tok.is(tok::l_brace)) {
|
||||
Diag(Tok, diag::err_expected_lbrace);
|
||||
return;
|
||||
}
|
||||
ConsumeBrace();
|
||||
|
||||
// Condition is false skip all inside the {}.
|
||||
if (!Result) {
|
||||
SkipUntil(tok::r_brace, false);
|
||||
ParsedAttributes Attrs(AttrFactory);
|
||||
StmtResult Compound = ParseCompoundStatement(Attrs);
|
||||
// FIXME: We're dropping these statements on the floor.
|
||||
return;
|
||||
}
|
||||
|
||||
BalancedDelimiterTracker Braces(*this, tok::l_brace);
|
||||
if (Braces.consumeOpen()) {
|
||||
Diag(Tok, diag::err_expected_lbrace);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (Result.Behavior) {
|
||||
case IEB_Parse:
|
||||
// Parse the statements below.
|
||||
break;
|
||||
|
||||
case IEB_Dependent:
|
||||
llvm_unreachable("Dependent case handled above");
|
||||
break;
|
||||
|
||||
case IEB_Skip:
|
||||
Braces.skipToEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2158,10 +2182,5 @@ void Parser::ParseMicrosoftIfExistsStatement(StmtVector &Stmts) {
|
||||
if (R.isUsable())
|
||||
Stmts.push_back(R.release());
|
||||
}
|
||||
|
||||
if (Tok.isNot(tok::r_brace)) {
|
||||
Diag(Tok, diag::err_expected_rbrace);
|
||||
return;
|
||||
}
|
||||
ConsumeBrace();
|
||||
Braces.consumeClose();
|
||||
}
|
||||
|
||||
@@ -1470,68 +1470,83 @@ void Parser::CodeCompleteNaturalLanguage() {
|
||||
Actions.CodeCompleteNaturalLanguage();
|
||||
}
|
||||
|
||||
bool Parser::ParseMicrosoftIfExistsCondition(bool& Result) {
|
||||
bool Parser::ParseMicrosoftIfExistsCondition(IfExistsCondition& Result) {
|
||||
assert((Tok.is(tok::kw___if_exists) || Tok.is(tok::kw___if_not_exists)) &&
|
||||
"Expected '__if_exists' or '__if_not_exists'");
|
||||
Token Condition = Tok;
|
||||
SourceLocation IfExistsLoc = ConsumeToken();
|
||||
Result.IsIfExists = Tok.is(tok::kw___if_exists);
|
||||
Result.KeywordLoc = ConsumeToken();
|
||||
|
||||
BalancedDelimiterTracker T(*this, tok::l_paren);
|
||||
if (T.consumeOpen()) {
|
||||
Diag(Tok, diag::err_expected_lparen_after) << IfExistsLoc;
|
||||
SkipUntil(tok::semi);
|
||||
Diag(Tok, diag::err_expected_lparen_after)
|
||||
<< (Result.IsIfExists? "__if_exists" : "__if_not_exists");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse nested-name-specifier.
|
||||
CXXScopeSpec SS;
|
||||
ParseOptionalCXXScopeSpecifier(SS, ParsedType(), false);
|
||||
ParseOptionalCXXScopeSpecifier(Result.SS, ParsedType(), false);
|
||||
|
||||
// Check nested-name specifier.
|
||||
if (SS.isInvalid()) {
|
||||
SkipUntil(tok::semi);
|
||||
if (Result.SS.isInvalid()) {
|
||||
T.skipToEnd();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse the unqualified-id.
|
||||
UnqualifiedId Name;
|
||||
if (ParseUnqualifiedId(SS, false, true, true, ParsedType(), Name)) {
|
||||
SkipUntil(tok::semi);
|
||||
if (ParseUnqualifiedId(Result.SS, false, true, true, ParsedType(),
|
||||
Result.Name)) {
|
||||
T.skipToEnd();
|
||||
return true;
|
||||
}
|
||||
|
||||
T.consumeClose();
|
||||
if (T.getCloseLocation().isInvalid())
|
||||
if (T.consumeClose())
|
||||
return true;
|
||||
|
||||
// Check if the symbol exists.
|
||||
bool Exist = Actions.CheckMicrosoftIfExistsSymbol(SS, Name);
|
||||
switch (Actions.CheckMicrosoftIfExistsSymbol(getCurScope(), Result.SS,
|
||||
Result.Name)) {
|
||||
case Sema::IER_Exists:
|
||||
Result.Behavior = Result.IsIfExists ? IEB_Parse : IEB_Skip;
|
||||
break;
|
||||
|
||||
Result = ((Condition.is(tok::kw___if_exists) && Exist) ||
|
||||
(Condition.is(tok::kw___if_not_exists) && !Exist));
|
||||
case Sema::IER_DoesNotExist:
|
||||
Result.Behavior = !Result.IsIfExists ? IEB_Parse : IEB_Skip;
|
||||
break;
|
||||
|
||||
case Sema::IER_Dependent:
|
||||
Result.Behavior = IEB_Dependent;
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Parser::ParseMicrosoftIfExistsExternalDeclaration() {
|
||||
bool Result;
|
||||
IfExistsCondition Result;
|
||||
if (ParseMicrosoftIfExistsCondition(Result))
|
||||
return;
|
||||
|
||||
if (Tok.isNot(tok::l_brace)) {
|
||||
BalancedDelimiterTracker Braces(*this, tok::l_brace);
|
||||
if (Braces.consumeOpen()) {
|
||||
Diag(Tok, diag::err_expected_lbrace);
|
||||
return;
|
||||
}
|
||||
ConsumeBrace();
|
||||
|
||||
// Condition is false skip all inside the {}.
|
||||
if (!Result) {
|
||||
SkipUntil(tok::r_brace, false);
|
||||
switch (Result.Behavior) {
|
||||
case IEB_Parse:
|
||||
// Parse declarations below.
|
||||
break;
|
||||
|
||||
case IEB_Dependent:
|
||||
llvm_unreachable("Cannot have a dependent external declaration");
|
||||
|
||||
case IEB_Skip:
|
||||
Braces.skipToEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
// Condition is true, parse the declaration.
|
||||
while (Tok.isNot(tok::r_brace)) {
|
||||
// Parse the declarations.
|
||||
while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
|
||||
ParsedAttributesWithRange attrs(AttrFactory);
|
||||
MaybeParseCXX0XAttributes(attrs);
|
||||
MaybeParseMicrosoftAttributes(attrs);
|
||||
@@ -1539,12 +1554,7 @@ void Parser::ParseMicrosoftIfExistsExternalDeclaration() {
|
||||
if (Result && !getCurScope()->getParent())
|
||||
Actions.getASTConsumer().HandleTopLevelDecl(Result.get());
|
||||
}
|
||||
|
||||
if (Tok.isNot(tok::r_brace)) {
|
||||
Diag(Tok, diag::err_expected_rbrace);
|
||||
return;
|
||||
}
|
||||
ConsumeBrace();
|
||||
Braces.consumeClose();
|
||||
}
|
||||
|
||||
Parser::DeclGroupPtrTy Parser::ParseModuleImport() {
|
||||
@@ -1629,3 +1639,8 @@ bool Parser::BalancedDelimiterTracker::consumeClose() {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Parser::BalancedDelimiterTracker::skipToEnd() {
|
||||
P.SkipUntil(Close, false);
|
||||
Cleanup = false;
|
||||
}
|
||||
|
||||
@@ -4665,17 +4665,37 @@ StmtResult Sema::ActOnFinishFullStmt(Stmt *FullStmt) {
|
||||
return MaybeCreateStmtWithCleanups(FullStmt);
|
||||
}
|
||||
|
||||
bool Sema::CheckMicrosoftIfExistsSymbol(CXXScopeSpec &SS,
|
||||
Sema::IfExistsResult Sema::CheckMicrosoftIfExistsSymbol(Scope *S,
|
||||
CXXScopeSpec &SS,
|
||||
UnqualifiedId &Name) {
|
||||
DeclarationNameInfo TargetNameInfo = GetNameFromUnqualifiedId(Name);
|
||||
DeclarationName TargetName = TargetNameInfo.getName();
|
||||
if (!TargetName)
|
||||
return false;
|
||||
return IER_DoesNotExist;
|
||||
|
||||
// If the name itself is dependent, then the result is dependent.
|
||||
if (TargetName.isDependentName())
|
||||
return IER_Dependent;
|
||||
|
||||
// Do the redeclaration lookup in the current scope.
|
||||
LookupResult R(*this, TargetNameInfo, Sema::LookupAnyName,
|
||||
Sema::NotForRedeclaration);
|
||||
LookupParsedName(R, S, &SS);
|
||||
R.suppressDiagnostics();
|
||||
LookupParsedName(R, getCurScope(), &SS);
|
||||
return !R.empty();
|
||||
|
||||
switch (R.getResultKind()) {
|
||||
case LookupResult::Found:
|
||||
case LookupResult::FoundOverloaded:
|
||||
case LookupResult::FoundUnresolvedValue:
|
||||
case LookupResult::Ambiguous:
|
||||
return IER_Exists;
|
||||
|
||||
case LookupResult::NotFound:
|
||||
return IER_DoesNotExist;
|
||||
|
||||
case LookupResult::NotFoundInCurrentInstantiation:
|
||||
return IER_Dependent;
|
||||
}
|
||||
|
||||
return IER_DoesNotExist;
|
||||
}
|
||||
|
||||
53
clang/test/SemaTemplate/ms-if-exists.cpp
Normal file
53
clang/test/SemaTemplate/ms-if-exists.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
// RUN: %clang_cc1 -fms-extensions %s -verify
|
||||
|
||||
struct Nontemplate {
|
||||
typedef int type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct X {
|
||||
__if_exists(Nontemplate::type) {
|
||||
typedef Nontemplate::type type;
|
||||
}
|
||||
|
||||
__if_exists(Nontemplate::value) {
|
||||
typedef Nontemplate::value type2;
|
||||
}
|
||||
|
||||
__if_not_exists(Nontemplate::value) {
|
||||
typedef int type3;
|
||||
}
|
||||
|
||||
__if_exists(T::X) { // expected-warning{{dependent __if_exists declarations are ignored}}
|
||||
typedef T::X type4;
|
||||
}
|
||||
};
|
||||
|
||||
X<int>::type i1;
|
||||
X<int>::type2 i2; // expected-error{{no type named 'type2' in 'X<int>'}}
|
||||
X<int>::type3 i3;
|
||||
X<int>::type4 i4; // expected-error{{no type named 'type4' in 'X<int>'}}
|
||||
|
||||
struct HasFoo {
|
||||
void foo();
|
||||
};
|
||||
struct HasBar {
|
||||
void bar(int);
|
||||
void bar(float);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void f(T t) {
|
||||
__if_exists(T::foo) {
|
||||
{ }
|
||||
t.foo();
|
||||
}
|
||||
|
||||
__if_not_exists(T::bar) {
|
||||
int *i = t;
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
template void f(HasFoo);
|
||||
template void f(HasBar);
|
||||
Reference in New Issue
Block a user