Implement support for dependent Microsoft __if_exists/__if_not_exists

statements. As noted in the documentation for the AST node, the
semantics of __if_exists/__if_not_exists are somewhat different from
the way Visual C++ implements them, because our parsed-template
representation can't accommodate VC++ semantics without serious
contortions. Hopefully this implementation is "good enough".

llvm-svn: 142901
This commit is contained in:
Douglas Gregor 2011-10-25 01:33:02 +00:00
parent 4223b3b380
commit deb4a2be67
21 changed files with 359 additions and 16 deletions

View File

@ -1735,6 +1735,9 @@ DEF_TRAVERSE_STMT(ObjCAtTryStmt, { })
DEF_TRAVERSE_STMT(ObjCForCollectionStmt, { })
DEF_TRAVERSE_STMT(ObjCAutoreleasePoolStmt, { })
DEF_TRAVERSE_STMT(CXXForRangeStmt, { })
DEF_TRAVERSE_STMT(MSDependentExistsStmt, {
TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
})
DEF_TRAVERSE_STMT(ReturnStmt, { })
DEF_TRAVERSE_STMT(SwitchStmt, { })
DEF_TRAVERSE_STMT(WhileStmt, { })

View File

@ -1596,7 +1596,6 @@ public:
}
static bool classof(SEHTryStmt *) { return true; }
};
} // end namespace clang

View File

@ -201,6 +201,91 @@ public:
}
};
/// \brief Representation of a Microsoft __if_exists or __if_not_exists
/// statement with a dependent name.
///
/// The __if_exists statement can be used to include a sequence of statements
/// in the program only when a particular dependent name does not exist. For
/// example:
///
/// \code
/// template<typename T>
/// void call_foo(T &t) {
/// __if_exists (T::foo) {
/// t.foo(); // okay: only called when T::foo exists.
/// }
/// }
/// \endcode
///
/// Similarly, the __if_not_exists statement can be used to include the
/// statements when a particular name does not exist.
///
/// Note that this statement only captures __if_exists and __if_not_exists
/// statements whose name is dependent. All non-dependent cases are handled
/// directly in the parser, so that they don't introduce a new scope. Clang
/// introduces scopes in the dependent case to keep names inside the compound
/// statement from leaking out into the surround statements, which would
/// compromise the template instantiation model. This behavior differs from
/// Visual C++ (which never introduces a scope), but is a fairly reasonable
/// approximation of the VC++ behavior.
class MSDependentExistsStmt : public Stmt {
SourceLocation KeywordLoc;
bool IsIfExists;
NestedNameSpecifierLoc QualifierLoc;
DeclarationNameInfo NameInfo;
Stmt *SubStmt;
friend class ASTReader;
friend class ASTStmtReader;
public:
MSDependentExistsStmt(SourceLocation KeywordLoc, bool IsIfExists,
NestedNameSpecifierLoc QualifierLoc,
DeclarationNameInfo NameInfo,
CompoundStmt *SubStmt)
: Stmt(MSDependentExistsStmtClass),
KeywordLoc(KeywordLoc), IsIfExists(IsIfExists),
QualifierLoc(QualifierLoc), NameInfo(NameInfo),
SubStmt(reinterpret_cast<Stmt *>(SubStmt)) { }
/// \brief Retrieve the location of the __if_exists or __if_not_exists
/// keyword.
SourceLocation getKeywordLoc() const { return KeywordLoc; }
/// \brief Determine whether this is an __if_exists statement.
bool isIfExists() const { return IsIfExists; }
/// \brief Determine whether this is an __if_exists statement.
bool isIfNotExists() const { return !IsIfExists; }
/// \brief Retrieve the nested-name-specifier that qualifies this name, if
/// any.
NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; }
/// \brief Retrieve the name of the entity we're testing for, along with
/// location information
DeclarationNameInfo getNameInfo() const { return NameInfo; }
/// \brief Retrieve the compound statement that will be included in the
/// program only if the existence of the symbol matches the initial keyword.
CompoundStmt *getSubStmt() const {
return reinterpret_cast<CompoundStmt *>(SubStmt);
}
SourceRange getSourceRange() const {
return SourceRange(KeywordLoc, SubStmt->getLocEnd());
}
child_range children() {
return child_range(&SubStmt, &SubStmt+1);
}
static bool classof(const Stmt *T) {
return T->getStmtClass() == MSDependentExistsStmtClass;
}
static bool classof(MSDependentExistsStmt *) { return true; }
};
} // end namespace clang

View File

@ -155,6 +155,7 @@ def CXXUuidofExpr : DStmt<Expr>;
def SEHTryStmt : Stmt;
def SEHExceptStmt : Stmt;
def SEHFinallyStmt : Stmt;
def MSDependentExistsStmt : Stmt;
// OpenCL Extensions.
def AsTypeExpr : DStmt<Expr>;

View File

@ -2603,9 +2603,23 @@ public:
IER_Dependent
};
IfExistsResult
CheckMicrosoftIfExistsSymbol(Scope *S, CXXScopeSpec &SS,
const DeclarationNameInfo &TargetNameInfo);
IfExistsResult
CheckMicrosoftIfExistsSymbol(Scope *S, CXXScopeSpec &SS, UnqualifiedId &Name);
StmtResult BuildMSDependentExistsStmt(SourceLocation KeywordLoc,
bool IsIfExists,
NestedNameSpecifierLoc QualifierLoc,
DeclarationNameInfo NameInfo,
Stmt *Nested);
StmtResult ActOnMSDependentExistsStmt(SourceLocation KeywordLoc,
bool IsIfExists,
CXXScopeSpec &SS, UnqualifiedId &Name,
Stmt *Nested);
//===------------------------- "Block" Extension ------------------------===//
/// ActOnBlockStart - This callback is invoked when a block literal is

View File

@ -1099,7 +1099,9 @@ namespace clang {
STMT_SEH_TRY, // SEHTryStmt
// ARC
EXPR_OBJC_BRIDGED_CAST // ObjCBridgedCastExpr
EXPR_OBJC_BRIDGED_CAST, // ObjCBridgedCastExpr
STMT_MS_DEPENDENT_EXISTS // MSDependentExistsStmt
};
/// \brief The kinds of designators that can occur in a

View File

@ -305,6 +305,22 @@ void StmtPrinter::VisitCXXForRangeStmt(CXXForRangeStmt *Node) {
Indent() << "}\n";
}
void StmtPrinter::VisitMSDependentExistsStmt(MSDependentExistsStmt *Node) {
Indent();
if (Node->isIfExists())
OS << "__if_exists (";
else
OS << "__if_not_exists (";
if (NestedNameSpecifier *Qualifier
= Node->getQualifierLoc().getNestedNameSpecifier())
Qualifier->print(OS, Policy);
OS << Node->getNameInfo() << ") ";
PrintRawCompoundStmt(Node->getSubStmt());
}
void StmtPrinter::VisitGotoStmt(GotoStmt *Node) {
Indent() << "goto " << Node->getLabel()->getName() << ";\n";
}

View File

@ -182,6 +182,13 @@ void StmtProfiler::VisitCXXForRangeStmt(const CXXForRangeStmt *S) {
VisitStmt(S);
}
void StmtProfiler::VisitMSDependentExistsStmt(const MSDependentExistsStmt *S) {
VisitStmt(S);
ID.AddBoolean(S->isIfExists());
VisitNestedNameSpecifier(S->getQualifierLoc().getNestedNameSpecifier());
VisitName(S->getNameInfo().getName());
}
void StmtProfiler::VisitSEHTryStmt(const SEHTryStmt *S) {
VisitStmt(S);
}

View File

@ -73,6 +73,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S) {
case Stmt::CXXCatchStmtClass:
case Stmt::SEHExceptStmtClass:
case Stmt::SEHFinallyStmtClass:
case Stmt::MSDependentExistsStmtClass:
llvm_unreachable("invalid statement class to emit generically");
case Stmt::NullStmtClass:
case Stmt::CompoundStmtClass:

View File

@ -2152,7 +2152,16 @@ void Parser::ParseMicrosoftIfExistsStatement(StmtVector &Stmts) {
ParsedAttributes Attrs(AttrFactory);
StmtResult Compound = ParseCompoundStatement(Attrs);
// FIXME: We're dropping these statements on the floor.
if (Compound.isInvalid())
return;
StmtResult DepResult = Actions.ActOnMSDependentExistsStmt(Result.KeywordLoc,
Result.IsIfExists,
Result.SS,
Result.Name,
Compound.get());
if (DepResult.isUsable())
Stmts.push_back(DepResult.get());
return;
}

View File

@ -4665,10 +4665,10 @@ StmtResult Sema::ActOnFinishFullStmt(Stmt *FullStmt) {
return MaybeCreateStmtWithCleanups(FullStmt);
}
Sema::IfExistsResult Sema::CheckMicrosoftIfExistsSymbol(Scope *S,
Sema::IfExistsResult
Sema::CheckMicrosoftIfExistsSymbol(Scope *S,
CXXScopeSpec &SS,
UnqualifiedId &Name) {
DeclarationNameInfo TargetNameInfo = GetNameFromUnqualifiedId(Name);
const DeclarationNameInfo &TargetNameInfo) {
DeclarationName TargetName = TargetNameInfo.getName();
if (!TargetName)
return IER_DoesNotExist;
@ -4699,3 +4699,11 @@ Sema::IfExistsResult Sema::CheckMicrosoftIfExistsSymbol(Scope *S,
return IER_DoesNotExist;
}
Sema::IfExistsResult Sema::CheckMicrosoftIfExistsSymbol(Scope *S,
CXXScopeSpec &SS,
UnqualifiedId &Name) {
DeclarationNameInfo TargetNameInfo = GetNameFromUnqualifiedId(Name);
return CheckMicrosoftIfExistsSymbol(S, SS, TargetNameInfo);
}

View File

@ -2484,3 +2484,26 @@ Sema::ActOnSEHFinallyBlock(SourceLocation Loc,
assert(Block);
return Owned(SEHFinallyStmt::Create(Context,Loc,Block));
}
StmtResult Sema::BuildMSDependentExistsStmt(SourceLocation KeywordLoc,
bool IsIfExists,
NestedNameSpecifierLoc QualifierLoc,
DeclarationNameInfo NameInfo,
Stmt *Nested)
{
return new (Context) MSDependentExistsStmt(KeywordLoc, IsIfExists,
QualifierLoc, NameInfo,
cast<CompoundStmt>(Nested));
}
StmtResult Sema::ActOnMSDependentExistsStmt(SourceLocation KeywordLoc,
bool IsIfExists,
CXXScopeSpec &SS,
UnqualifiedId &Name,
Stmt *Nested) {
return BuildMSDependentExistsStmt(KeywordLoc, IsIfExists,
SS.getWithLocInContext(Context),
GetNameFromUnqualifiedId(Name),
Nested);
}

View File

@ -1292,6 +1292,19 @@ public:
Cond, Inc, LoopVar, RParenLoc);
}
/// \brief Build a new C++0x range-based for statement.
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
StmtResult RebuildMSDependentExistsStmt(SourceLocation KeywordLoc,
bool IsIfExists,
NestedNameSpecifierLoc QualifierLoc,
DeclarationNameInfo NameInfo,
Stmt *Nested) {
return getSema().BuildMSDependentExistsStmt(KeywordLoc, IsIfExists,
QualifierLoc, NameInfo, Nested);
}
/// \brief Attach body to a C++0x range-based for statement.
///
/// By default, performs semantic analysis to finish the new statement.
@ -5745,6 +5758,72 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
return FinishCXXForRangeStmt(NewStmt.get(), Body.get());
}
template<typename Derived>
StmtResult
TreeTransform<Derived>::TransformMSDependentExistsStmt(
MSDependentExistsStmt *S) {
// Transform the nested-name-specifier, if any.
NestedNameSpecifierLoc QualifierLoc;
if (S->getQualifierLoc()) {
QualifierLoc
= getDerived().TransformNestedNameSpecifierLoc(S->getQualifierLoc());
if (!QualifierLoc)
return StmtError();
}
// Transform the declaration name.
DeclarationNameInfo NameInfo = S->getNameInfo();
if (NameInfo.getName()) {
NameInfo = getDerived().TransformDeclarationNameInfo(NameInfo);
if (!NameInfo.getName())
return StmtError();
}
// Check whether anything changed.
if (!getDerived().AlwaysRebuild() &&
QualifierLoc == S->getQualifierLoc() &&
NameInfo.getName() == S->getNameInfo().getName())
return S;
// Determine whether this name exists, if we can.
CXXScopeSpec SS;
SS.Adopt(QualifierLoc);
bool Dependent = false;
switch (getSema().CheckMicrosoftIfExistsSymbol(/*S=*/0, SS, NameInfo)) {
case Sema::IER_Exists:
if (S->isIfExists())
break;
return new (getSema().Context) NullStmt(S->getKeywordLoc());
case Sema::IER_DoesNotExist:
if (S->isIfNotExists())
break;
return new (getSema().Context) NullStmt(S->getKeywordLoc());
case Sema::IER_Dependent:
Dependent = true;
break;
}
// We need to continue with the instantiation, so do so now.
StmtResult SubStmt = getDerived().TransformCompoundStmt(S->getSubStmt());
if (SubStmt.isInvalid())
return StmtError();
// If we have resolved the name, just transform to the substatement.
if (!Dependent)
return SubStmt;
// The name is still dependent, so build a dependent expression again.
return getDerived().RebuildMSDependentExistsStmt(S->getKeywordLoc(),
S->isIfExists(),
QualifierLoc,
NameInfo,
SubStmt.get());
}
template<typename Derived>
StmtResult
TreeTransform<Derived>::TransformSEHTryStmt(SEHTryStmt *S) {

View File

@ -992,6 +992,15 @@ void ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
S->setBody(Reader.ReadSubStmt());
}
void ASTStmtReader::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) {
VisitStmt(S);
S->KeywordLoc = ReadSourceLocation(Record, Idx);
S->IsIfExists = Record[Idx++];
S->QualifierLoc = Reader.ReadNestedNameSpecifierLoc(F, Record, Idx);
ReadDeclarationNameInfo(S->NameInfo, Record, Idx);
S->SubStmt = Reader.ReadSubStmt();
}
void ASTStmtReader::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
VisitCallExpr(E);
E->setOperator((OverloadedOperatorKind)Record[Idx++]);
@ -1847,6 +1856,13 @@ Stmt *ASTReader::ReadStmtFromStream(Module &F) {
S = new (Context) CXXForRangeStmt(Empty);
break;
case STMT_MS_DEPENDENT_EXISTS:
S = new (Context) MSDependentExistsStmt(SourceLocation(), true,
NestedNameSpecifierLoc(),
DeclarationNameInfo(),
0);
break;
case EXPR_CXX_OPERATOR_CALL:
S = new (Context) CXXOperatorCallExpr(Context, Empty);
break;

View File

@ -957,6 +957,16 @@ void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
Code = serialization::STMT_CXX_FOR_RANGE;
}
void ASTStmtWriter::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) {
VisitStmt(S);
Writer.AddSourceLocation(S->getKeywordLoc(), Record);
Record.push_back(S->isIfExists());
Writer.AddNestedNameSpecifierLoc(S->getQualifierLoc(), Record);
Writer.AddDeclarationNameInfo(S->getNameInfo(), Record);
Writer.AddStmt(S->getSubStmt());
Code = serialization::STMT_MS_DEPENDENT_EXISTS;
}
void ASTStmtWriter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
VisitCallExpr(E);
Record.push_back(E->getOperator());

View File

@ -556,6 +556,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::NullStmtClass:
case Stmt::SwitchStmtClass:
case Stmt::WhileStmtClass:
case Expr::MSDependentExistsStmtClass:
llvm_unreachable("Stmt should not be in analyzer evaluation loop");
break;

View File

@ -0,0 +1,24 @@
template<typename T>
void f(T t) {
__if_exists(T::foo) {
{ }
t.foo();
}
__if_not_exists(T::bar) {
int *i = t; // expected-error{{no viable conversion from 'HasFoo' to 'int *'}}
{ }
}
}
// RUN: c-index-test -test-annotate-tokens=%s:3:1:11:3 -fms-extensions %s | FileCheck %s
// CHECK: Identifier: "T" [3:15 - 3:16] TypeRef=T:1:19
// CHECK: Punctuation: "}" [4:7 - 4:8] CompoundStmt=
// CHECK: Identifier: "t" [5:5 - 5:6] DeclRefExpr=t:2:10
// CHECK: Punctuation: "." [5:6 - 5:7] MemberRefExpr=
// CHECK: Identifier: "foo" [5:7 - 5:10] MemberRefExpr=
// CHECK: Keyword: "int" [9:5 - 9:8] VarDecl=i:9:10 (Definition)
// CHECK: Punctuation: "*" [9:9 - 9:10] VarDecl=i:9:10 (Definition)
// CHECK: Identifier: "i" [9:10 - 9:11] VarDecl=i:9:10 (Definition)
// CHECK: Punctuation: "=" [9:12 - 9:13] VarDecl=i:9:10 (Definition)

View File

@ -0,0 +1,29 @@
// RUN: %clang_cc1 -x c++ -fms-extensions -fsyntax-only -emit-pch -o %t %s
// RUN: %clang_cc1 -x c++ -fms-extensions -fsyntax-only -include-pch %t %s -verify
#ifndef HEADER
#define HEADER
template<typename T>
void f(T t) {
__if_exists(T::foo) {
{ }
t.foo();
}
__if_not_exists(T::bar) {
int *i = t; // expected-error{{no viable conversion from 'HasFoo' to 'int *'}}
{ }
}
}
#else
struct HasFoo {
void foo();
};
struct HasBar {
void bar(int);
void bar(float);
};
template void f(HasFoo); // expected-note{{in instantiation of function template specialization 'f<HasFoo>' requested here}}
template void f(HasBar);
#endif

View File

@ -44,10 +44,10 @@ void f(T t) {
}
__if_not_exists(T::bar) {
int *i = t;
int *i = t; // expected-error{{no viable conversion from 'HasFoo' to 'int *'}}
{ }
}
}
template void f(HasFoo);
template void f(HasFoo); // expected-note{{in instantiation of function template specialization 'f<HasFoo>' requested here}}
template void f(HasBar);

View File

@ -1754,6 +1754,8 @@ public:
switch (S->getStmtClass()) {
default:
llvm_unreachable("Unhandled Stmt");
case clang::Stmt::MSDependentExistsStmtClass:
return cast<MSDependentExistsStmt>(S)->getNameInfo();
case Stmt::CXXDependentScopeMemberExprClass:
return cast<CXXDependentScopeMemberExpr>(S)->getMemberNameInfo();
case Stmt::DependentScopeDeclRefExprClass:
@ -1788,6 +1790,7 @@ public:
void VisitCompoundLiteralExpr(CompoundLiteralExpr *E);
void VisitCompoundStmt(CompoundStmt *S);
void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { /* Do nothing. */ }
void VisitMSDependentExistsStmt(MSDependentExistsStmt *S);
void VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E);
void VisitCXXNewExpr(CXXNewExpr *E);
void VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *E);
@ -1897,6 +1900,14 @@ void EnqueueVisitor::VisitCompoundStmt(CompoundStmt *S) {
AddStmt(*I);
}
}
void EnqueueVisitor::
VisitMSDependentExistsStmt(MSDependentExistsStmt *S) {
AddStmt(S->getSubStmt());
AddDeclarationNameInfo(S);
if (NestedNameSpecifierLoc QualifierLoc = S->getQualifierLoc())
AddNestedNameSpecifierLoc(QualifierLoc);
}
void EnqueueVisitor::
VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) {
AddExplicitTemplateArgs(E->getOptionalExplicitTemplateArgs());

View File

@ -430,7 +430,7 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU,
K = CXCursor_CallExpr;
break;
case Stmt::ObjCMessageExprClass:
case Stmt::ObjCMessageExprClass: {
K = CXCursor_ObjCMessageExpr;
int SelectorIdIndex = -1;
// Check if cursor points to a selector id.
@ -447,6 +447,11 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU,
return getSelectorIdentifierCursor(SelectorIdIndex, C);
}
case Stmt::MSDependentExistsStmtClass:
K = CXCursor_UnexposedStmt;
break;
}
CXCursor C = { K, 0, { Parent, S, TU } };
return C;
}