forked from OSchip/llvm-project
Implement C++11 [expr.prim.general]p3, which permits the use of 'this'
in the declaration of a non-static member function after the (optional) cv-qualifier-seq, which in practice means in the exception specification and late-specified return type. The new scheme here used to manage 'this' outside of a member function scope is more general than the Scope-based mechanism previously used for non-static data member initializers and late-parsesd attributes, because it can also handle the cv-qualifiers on the member function. Note, however, that a separate pass is required for static member functions to determine whether 'this' was used, because we might not know that we have a static function until after declaration matching. Finally, this introduces name mangling for 'this' and for the implicit 'this', which is intended to match GCC's mangling. Independent verification for the new mangling test case would be appreciated. Fixes PR10036 and PR12450. llvm-svn: 154799
This commit is contained in:
parent
26d7a94981
commit
3024f07c12
|
@ -3801,6 +3801,9 @@ def warn_null_in_comparison_operation : Warning<
|
|||
|
||||
def err_invalid_this_use : Error<
|
||||
"invalid use of 'this' outside of a non-static member function">;
|
||||
def err_this_static_member_func : Error<
|
||||
"'this' cannot be%select{| implicitly}0 used in a static member function "
|
||||
"declaration">;
|
||||
def err_invalid_member_use_in_static_method : Error<
|
||||
"invalid use of member %0 in static member function">;
|
||||
def err_invalid_qualified_function_type : Error<
|
||||
|
|
|
@ -81,13 +81,8 @@ public:
|
|||
/// SwitchScope - This is a scope that corresponds to a switch statement.
|
||||
SwitchScope = 0x800,
|
||||
|
||||
/// ThisScope - This is the scope of a struct/union/class definition,
|
||||
/// outside of any member function definition, where 'this' is nonetheless
|
||||
/// usable.
|
||||
ThisScope = 0x1000,
|
||||
|
||||
/// TryScope - This is the scope of a C++ try statement.
|
||||
TryScope = 0x2000
|
||||
TryScope = 0x1000
|
||||
};
|
||||
private:
|
||||
/// The parent scope for this scope. This is null for the translation-unit
|
||||
|
|
|
@ -446,11 +446,13 @@ public:
|
|||
Sema &S;
|
||||
DeclContext *SavedContext;
|
||||
ProcessingContextState SavedContextState;
|
||||
|
||||
QualType SavedCXXThisTypeOverride;
|
||||
|
||||
public:
|
||||
ContextRAII(Sema &S, DeclContext *ContextToPush)
|
||||
: S(S), SavedContext(S.CurContext),
|
||||
SavedContextState(S.DelayedDiagnostics.pushContext())
|
||||
SavedContextState(S.DelayedDiagnostics.pushContext()),
|
||||
SavedCXXThisTypeOverride(S.CXXThisTypeOverride)
|
||||
{
|
||||
assert(ContextToPush && "pushing null context");
|
||||
S.CurContext = ContextToPush;
|
||||
|
@ -460,6 +462,7 @@ public:
|
|||
if (!SavedContext) return;
|
||||
S.CurContext = SavedContext;
|
||||
S.DelayedDiagnostics.popContext(SavedContextState);
|
||||
S.CXXThisTypeOverride = SavedCXXThisTypeOverride;
|
||||
SavedContext = 0;
|
||||
}
|
||||
|
||||
|
@ -646,7 +649,7 @@ public:
|
|||
|
||||
/// A stack of expression evaluation contexts.
|
||||
SmallVector<ExpressionEvaluationContextRecord, 8> ExprEvalContexts;
|
||||
|
||||
|
||||
/// SpecialMemberOverloadResult - The overloading result for a special member
|
||||
/// function.
|
||||
///
|
||||
|
@ -3247,6 +3250,18 @@ public:
|
|||
/// special member function.
|
||||
bool isImplicitlyDeleted(FunctionDecl *FD);
|
||||
|
||||
/// \brief Check wither 'this' shows up in the type of a static member
|
||||
/// function after the (naturally empty) cv-qualifier-seq would be.
|
||||
///
|
||||
/// \returns true if an error occurred.
|
||||
bool checkThisInStaticMemberFunctionType(CXXMethodDecl *Method);
|
||||
|
||||
/// \brief Check wither 'this' shows up in the attributes of the given
|
||||
/// static member function.
|
||||
///
|
||||
/// \returns true if an error occurred.
|
||||
bool checkThisInStaticMemberFunctionAttributes(CXXMethodDecl *Method);
|
||||
|
||||
/// MaybeBindToTemporary - If the passed in expression has a record type with
|
||||
/// a non-trivial destructor, this will return CXXBindTemporaryExpr. Otherwise
|
||||
/// it simply returns the passed in expression.
|
||||
|
@ -3328,6 +3343,29 @@ public:
|
|||
/// \returns The type of 'this', if possible. Otherwise, returns a NULL type.
|
||||
QualType getCurrentThisType();
|
||||
|
||||
/// \brief When non-NULL, the C++ 'this' expression is allowed despite the
|
||||
/// current context not being a non-static member function. In such cases,
|
||||
/// this provides the type used for 'this'.
|
||||
QualType CXXThisTypeOverride;
|
||||
|
||||
/// \brief RAII object used to temporarily allow the C++ 'this' expression
|
||||
/// to be used, with the given qualifiers on the current class type.
|
||||
class CXXThisScopeRAII {
|
||||
Sema &S;
|
||||
QualType OldCXXThisTypeOverride;
|
||||
bool Enabled;
|
||||
|
||||
public:
|
||||
/// \brief Introduce a new scope where 'this' may be allowed (when enabled),
|
||||
/// using the given declaration (which is either a class template or a
|
||||
/// class) along with the given qualifiers.
|
||||
/// along with the qualifiers placed on '*this'.
|
||||
CXXThisScopeRAII(Sema &S, Decl *ContextDecl, unsigned CXXThisTypeQuals,
|
||||
bool Enabled = true);
|
||||
|
||||
~CXXThisScopeRAII();
|
||||
};
|
||||
|
||||
/// \brief Make sure the value of 'this' is actually available in the current
|
||||
/// context, if it is a potentially evaluated context.
|
||||
///
|
||||
|
@ -3337,6 +3375,11 @@ public:
|
|||
/// capture list.
|
||||
void CheckCXXThisCapture(SourceLocation Loc, bool Explicit = false);
|
||||
|
||||
/// \brief Determine whether the given type is the type of *this that is used
|
||||
/// outside of the body of a member function for a type that is currently
|
||||
/// being defined.
|
||||
bool isThisOutsideMemberFunctionBody(QualType BaseType);
|
||||
|
||||
/// ActOnCXXBoolLiteral - Parse {true,false} literals.
|
||||
ExprResult ActOnCXXBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind);
|
||||
|
||||
|
@ -5481,7 +5524,9 @@ public:
|
|||
TypeSourceInfo *SubstFunctionDeclType(TypeSourceInfo *T,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
SourceLocation Loc,
|
||||
DeclarationName Entity);
|
||||
DeclarationName Entity,
|
||||
CXXRecordDecl *ThisContext,
|
||||
unsigned ThisTypeQuals);
|
||||
ParmVarDecl *SubstParmVarDecl(ParmVarDecl *D,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
int indexAdjustment,
|
||||
|
|
|
@ -2280,9 +2280,7 @@ void CXXNameMangler::mangleIntegerLiteral(QualType T,
|
|||
|
||||
}
|
||||
|
||||
/// Mangles a member expression. Implicit accesses are not handled,
|
||||
/// but that should be okay, because you shouldn't be able to
|
||||
/// make an implicit access in a function template declaration.
|
||||
/// Mangles a member expression.
|
||||
void CXXNameMangler::mangleMemberExpr(const Expr *base,
|
||||
bool isArrow,
|
||||
NestedNameSpecifier *qualifier,
|
||||
|
@ -2291,8 +2289,17 @@ void CXXNameMangler::mangleMemberExpr(const Expr *base,
|
|||
unsigned arity) {
|
||||
// <expression> ::= dt <expression> <unresolved-name>
|
||||
// ::= pt <expression> <unresolved-name>
|
||||
Out << (isArrow ? "pt" : "dt");
|
||||
mangleExpression(base);
|
||||
if (base) {
|
||||
if (base->isImplicitCXXThis()) {
|
||||
// Note: GCC mangles member expressions to the implicit 'this' as
|
||||
// *this., whereas we represent them as this->. The Itanium C++ ABI
|
||||
// does not specify anything here, so we follow GCC.
|
||||
Out << "dtdefpT";
|
||||
} else {
|
||||
Out << (isArrow ? "pt" : "dt");
|
||||
mangleExpression(base);
|
||||
}
|
||||
}
|
||||
mangleUnresolvedName(qualifier, firstQualifierLookup, member, arity);
|
||||
}
|
||||
|
||||
|
@ -2346,6 +2353,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) {
|
|||
// <expr-primary> ::= L <type> <value number> E # integer literal
|
||||
// ::= L <type <value float> E # floating literal
|
||||
// ::= L <mangled-name> E # external name
|
||||
// ::= fpT # 'this' expression
|
||||
QualType ImplicitlyConvertedToType;
|
||||
|
||||
recurse:
|
||||
|
@ -2361,7 +2369,6 @@ recurse:
|
|||
// These all can only appear in local or variable-initialization
|
||||
// contexts and so should never appear in a mangling.
|
||||
case Expr::AddrLabelExprClass:
|
||||
case Expr::CXXThisExprClass:
|
||||
case Expr::DesignatedInitExprClass:
|
||||
case Expr::ImplicitValueInitExprClass:
|
||||
case Expr::ParenListExprClass:
|
||||
|
@ -2919,6 +2926,10 @@ recurse:
|
|||
mangleExpression(cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr());
|
||||
break;
|
||||
}
|
||||
|
||||
case Expr::CXXThisExprClass:
|
||||
Out << "fpT";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -763,6 +763,7 @@ void StmtProfiler::VisitCXXUuidofExpr(const CXXUuidofExpr *S) {
|
|||
|
||||
void StmtProfiler::VisitCXXThisExpr(const CXXThisExpr *S) {
|
||||
VisitExpr(S);
|
||||
ID.AddBoolean(S->isImplicit());
|
||||
}
|
||||
|
||||
void StmtProfiler::VisitCXXThrowExpr(const CXXThrowExpr *S) {
|
||||
|
|
|
@ -447,9 +447,9 @@ void Parser::ParseLexedMemberInitializers(ParsingClass &Class) {
|
|||
if (HasTemplateScope)
|
||||
Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate);
|
||||
|
||||
// Set or update the scope flags to include Scope::ThisScope.
|
||||
// Set or update the scope flags.
|
||||
bool AlreadyHasClassScope = Class.TopLevelClass;
|
||||
unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope|Scope::ThisScope;
|
||||
unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope;
|
||||
ParseScope ClassScope(this, ScopeFlags, !AlreadyHasClassScope);
|
||||
ParseScopeFlags ClassScopeFlags(this, ScopeFlags, AlreadyHasClassScope);
|
||||
|
||||
|
@ -457,10 +457,20 @@ void Parser::ParseLexedMemberInitializers(ParsingClass &Class) {
|
|||
Actions.ActOnStartDelayedMemberDeclarations(getCurScope(),
|
||||
Class.TagOrTemplate);
|
||||
|
||||
for (size_t i = 0; i < Class.LateParsedDeclarations.size(); ++i) {
|
||||
Class.LateParsedDeclarations[i]->ParseLexedMemberInitializers();
|
||||
}
|
||||
{
|
||||
// C++11 [expr.prim.general]p4:
|
||||
// Otherwise, if a member-declarator declares a non-static data member
|
||||
// (9.2) of a class X, the expression this is a prvalue of type "pointer
|
||||
// to X" within the optional brace-or-equal-initializer. It shall not
|
||||
// appear elsewhere in the member-declarator.
|
||||
Sema::CXXThisScopeRAII ThisScope(Actions, Class.TagOrTemplate,
|
||||
/*TypeQuals=*/(unsigned)0);
|
||||
|
||||
for (size_t i = 0; i < Class.LateParsedDeclarations.size(); ++i) {
|
||||
Class.LateParsedDeclarations[i]->ParseLexedMemberInitializers();
|
||||
}
|
||||
}
|
||||
|
||||
if (!AlreadyHasClassScope)
|
||||
Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(),
|
||||
Class.TagOrTemplate);
|
||||
|
@ -481,6 +491,7 @@ void Parser::ParseLexedMemberInitializer(LateParsedMemberInitializer &MI) {
|
|||
ConsumeAnyToken();
|
||||
|
||||
SourceLocation EqualLoc;
|
||||
|
||||
ExprResult Init = ParseCXXMemberInitializer(MI.Field, /*IsFunction=*/false,
|
||||
EqualLoc);
|
||||
|
||||
|
|
|
@ -729,9 +729,9 @@ void Parser::ParseLexedAttributes(ParsingClass &Class) {
|
|||
if (HasTemplateScope)
|
||||
Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate);
|
||||
|
||||
// Set or update the scope flags to include Scope::ThisScope.
|
||||
// Set or update the scope flags.
|
||||
bool AlreadyHasClassScope = Class.TopLevelClass;
|
||||
unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope|Scope::ThisScope;
|
||||
unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope;
|
||||
ParseScope ClassScope(this, ScopeFlags, !AlreadyHasClassScope);
|
||||
ParseScopeFlags ClassScopeFlags(this, ScopeFlags, AlreadyHasClassScope);
|
||||
|
||||
|
@ -739,11 +739,16 @@ void Parser::ParseLexedAttributes(ParsingClass &Class) {
|
|||
if (!AlreadyHasClassScope)
|
||||
Actions.ActOnStartDelayedMemberDeclarations(getCurScope(),
|
||||
Class.TagOrTemplate);
|
||||
|
||||
for (unsigned i = 0, ni = Class.LateParsedDeclarations.size(); i < ni; ++i) {
|
||||
Class.LateParsedDeclarations[i]->ParseLexedAttributes();
|
||||
{
|
||||
// Allow 'this' within late-parsed attributes.
|
||||
Sema::CXXThisScopeRAII ThisScope(Actions, Class.TagOrTemplate,
|
||||
/*TypeQuals=*/0);
|
||||
|
||||
for (unsigned i = 0, ni = Class.LateParsedDeclarations.size(); i < ni; ++i){
|
||||
Class.LateParsedDeclarations[i]->ParseLexedAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!AlreadyHasClassScope)
|
||||
Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(),
|
||||
Class.TagOrTemplate);
|
||||
|
@ -4242,6 +4247,23 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
|
|||
EndLoc = RefQualifierLoc;
|
||||
}
|
||||
|
||||
// C++11 [expr.prim.general]p3:
|
||||
// If a declaration declares a member function or member function
|
||||
// template of a class X, the expression this is a prvalue of type
|
||||
// "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq
|
||||
// and the end of the function-definition, member-declarator, or
|
||||
// declarator.
|
||||
bool IsCXX11MemberFunction =
|
||||
getLangOpts().CPlusPlus0x &&
|
||||
(D.getContext() == Declarator::MemberContext ||
|
||||
(D.getContext() == Declarator::FileContext &&
|
||||
D.getCXXScopeSpec().isValid() &&
|
||||
Actions.CurContext->isRecord()));
|
||||
Sema::CXXThisScopeRAII ThisScope(Actions,
|
||||
dyn_cast<CXXRecordDecl>(Actions.CurContext),
|
||||
DS.getTypeQualifiers(),
|
||||
IsCXX11MemberFunction);
|
||||
|
||||
// Parse exception-specification[opt].
|
||||
ESpecType = MaybeParseExceptionSpecification(ESpecRange,
|
||||
DynamicExceptions,
|
||||
|
|
|
@ -872,8 +872,6 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||
// FIXME: Rename BlockScope -> ClosureScope if we decide to continue using
|
||||
// it.
|
||||
unsigned ScopeFlags = Scope::BlockScope | Scope::FnScope | Scope::DeclScope;
|
||||
if (getCurScope()->getFlags() & Scope::ThisScope)
|
||||
ScopeFlags |= Scope::ThisScope;
|
||||
ParseScope BodyScope(this, ScopeFlags);
|
||||
|
||||
Actions.ActOnStartOfLambdaDefinition(Intro, D, getCurScope());
|
||||
|
|
|
@ -103,6 +103,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
|
|||
AnalysisWarnings(*this)
|
||||
{
|
||||
TUScope = 0;
|
||||
|
||||
LoadedExternalKnownNamespaces = false;
|
||||
for (unsigned I = 0; I != NSAPI::NumNSNumberLiteralMethods; ++I)
|
||||
NSNumberLiteralMethods[I] = 0;
|
||||
|
|
|
@ -5834,6 +5834,9 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Method->isStatic())
|
||||
checkThisInStaticMemberFunctionType(Method);
|
||||
}
|
||||
|
||||
// Extra checking for C++ overloaded operators (C++ [over.oper]).
|
||||
|
@ -7554,7 +7557,11 @@ void Sema::ActOnFinishDelayedAttribute(Scope *S, Decl *D,
|
|||
// Always attach attributes to the underlying decl.
|
||||
if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D))
|
||||
D = TD->getTemplatedDecl();
|
||||
ProcessDeclAttributeList(S, D, Attrs.getList());
|
||||
ProcessDeclAttributeList(S, D, Attrs.getList());
|
||||
|
||||
if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(D))
|
||||
if (Method->isStatic())
|
||||
checkThisInStaticMemberFunctionAttributes(Method);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "clang/AST/DeclVisitor.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/AST/RecordLayout.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/AST/StmtVisitor.h"
|
||||
#include "clang/AST/TypeLoc.h"
|
||||
#include "clang/AST/TypeOrdering.h"
|
||||
|
@ -11053,6 +11054,129 @@ void Sema::CheckDelegatingCtorCycles() {
|
|||
(*CI)->setInvalidDecl();
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// \brief AST visitor that finds references to the 'this' expression.
|
||||
class FindCXXThisExpr : public RecursiveASTVisitor<FindCXXThisExpr> {
|
||||
Sema &S;
|
||||
|
||||
public:
|
||||
explicit FindCXXThisExpr(Sema &S) : S(S) { }
|
||||
|
||||
bool VisitCXXThisExpr(CXXThisExpr *E) {
|
||||
S.Diag(E->getLocation(), diag::err_this_static_member_func)
|
||||
<< E->isImplicit();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bool Sema::checkThisInStaticMemberFunctionType(CXXMethodDecl *Method) {
|
||||
TypeSourceInfo *TSInfo = Method->getTypeSourceInfo();
|
||||
if (!TSInfo)
|
||||
return false;
|
||||
|
||||
TypeLoc TL = TSInfo->getTypeLoc();
|
||||
FunctionProtoTypeLoc *ProtoTL = dyn_cast<FunctionProtoTypeLoc>(&TL);
|
||||
if (!ProtoTL)
|
||||
return false;
|
||||
|
||||
// C++11 [expr.prim.general]p3:
|
||||
// [The expression this] shall not appear before the optional
|
||||
// cv-qualifier-seq and it shall not appear within the declaration of a
|
||||
// static member function (although its type and value category are defined
|
||||
// within a static member function as they are within a non-static member
|
||||
// function). [ Note: this is because declaration matching does not occur
|
||||
// until the complete declarator is known. — end note ]
|
||||
const FunctionProtoType *Proto = ProtoTL->getTypePtr();
|
||||
FindCXXThisExpr Finder(*this);
|
||||
|
||||
// If the return type came after the cv-qualifier-seq, check it now.
|
||||
if (Proto->hasTrailingReturn() &&
|
||||
!Finder.TraverseTypeLoc(ProtoTL->getResultLoc()))
|
||||
return true;
|
||||
|
||||
// Check the exception specification.
|
||||
switch (Proto->getExceptionSpecType()) {
|
||||
case EST_BasicNoexcept:
|
||||
case EST_Delayed:
|
||||
case EST_DynamicNone:
|
||||
case EST_MSAny:
|
||||
case EST_None:
|
||||
break;
|
||||
|
||||
case EST_ComputedNoexcept:
|
||||
if (!Finder.TraverseStmt(Proto->getNoexceptExpr()))
|
||||
return true;
|
||||
|
||||
case EST_Dynamic:
|
||||
for (FunctionProtoType::exception_iterator E = Proto->exception_begin(),
|
||||
EEnd = Proto->exception_end();
|
||||
E != EEnd; ++E) {
|
||||
if (!Finder.TraverseType(*E))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return checkThisInStaticMemberFunctionAttributes(Method);
|
||||
}
|
||||
|
||||
bool Sema::checkThisInStaticMemberFunctionAttributes(CXXMethodDecl *Method) {
|
||||
FindCXXThisExpr Finder(*this);
|
||||
|
||||
// Check attributes.
|
||||
for (Decl::attr_iterator A = Method->attr_begin(), AEnd = Method->attr_end();
|
||||
A != AEnd; ++A) {
|
||||
// FIXME: This should be emitted by tblgen.
|
||||
Expr *Arg = 0;
|
||||
ArrayRef<Expr *> Args;
|
||||
if (GuardedByAttr *G = dyn_cast<GuardedByAttr>(*A))
|
||||
Arg = G->getArg();
|
||||
else if (PtGuardedByAttr *G = dyn_cast<PtGuardedByAttr>(*A))
|
||||
Arg = G->getArg();
|
||||
else if (AcquiredAfterAttr *AA = dyn_cast<AcquiredAfterAttr>(*A))
|
||||
Args = ArrayRef<Expr *>(AA->args_begin(), AA->args_size());
|
||||
else if (AcquiredBeforeAttr *AB = dyn_cast<AcquiredBeforeAttr>(*A))
|
||||
Args = ArrayRef<Expr *>(AB->args_begin(), AB->args_size());
|
||||
else if (ExclusiveLockFunctionAttr *ELF
|
||||
= dyn_cast<ExclusiveLockFunctionAttr>(*A))
|
||||
Args = ArrayRef<Expr *>(ELF->args_begin(), ELF->args_size());
|
||||
else if (SharedLockFunctionAttr *SLF
|
||||
= dyn_cast<SharedLockFunctionAttr>(*A))
|
||||
Args = ArrayRef<Expr *>(SLF->args_begin(), SLF->args_size());
|
||||
else if (ExclusiveTrylockFunctionAttr *ETLF
|
||||
= dyn_cast<ExclusiveTrylockFunctionAttr>(*A)) {
|
||||
Arg = ETLF->getSuccessValue();
|
||||
Args = ArrayRef<Expr *>(ETLF->args_begin(), ETLF->args_size());
|
||||
} else if (SharedTrylockFunctionAttr *STLF
|
||||
= dyn_cast<SharedTrylockFunctionAttr>(*A)) {
|
||||
Arg = STLF->getSuccessValue();
|
||||
Args = ArrayRef<Expr *>(STLF->args_begin(), STLF->args_size());
|
||||
} else if (UnlockFunctionAttr *UF = dyn_cast<UnlockFunctionAttr>(*A))
|
||||
Args = ArrayRef<Expr *>(UF->args_begin(), UF->args_size());
|
||||
else if (LockReturnedAttr *LR = dyn_cast<LockReturnedAttr>(*A))
|
||||
Arg = LR->getArg();
|
||||
else if (LocksExcludedAttr *LE = dyn_cast<LocksExcludedAttr>(*A))
|
||||
Args = ArrayRef<Expr *>(LE->args_begin(), LE->args_size());
|
||||
else if (ExclusiveLocksRequiredAttr *ELR
|
||||
= dyn_cast<ExclusiveLocksRequiredAttr>(*A))
|
||||
Args = ArrayRef<Expr *>(ELR->args_begin(), ELR->args_size());
|
||||
else if (SharedLocksRequiredAttr *SLR
|
||||
= dyn_cast<SharedLocksRequiredAttr>(*A))
|
||||
Args = ArrayRef<Expr *>(SLR->args_begin(), SLR->args_size());
|
||||
|
||||
if (Arg && !Finder.TraverseStmt(Arg))
|
||||
return true;
|
||||
|
||||
for (unsigned I = 0, N = Args.size(); I != N; ++I) {
|
||||
if (!Finder.TraverseStmt(Args[I]))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// IdentifyCUDATarget - Determine the CUDA compilation target for this function
|
||||
Sema::CUDAFunctionTarget Sema::IdentifyCUDATarget(const FunctionDecl *D) {
|
||||
// Implicitly declared functions (e.g. copy constructors) are
|
||||
|
|
|
@ -654,23 +654,44 @@ ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E,
|
|||
|
||||
QualType Sema::getCurrentThisType() {
|
||||
DeclContext *DC = getFunctionLevelDeclContext();
|
||||
QualType ThisTy;
|
||||
QualType ThisTy = CXXThisTypeOverride;
|
||||
if (CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(DC)) {
|
||||
if (method && method->isInstance())
|
||||
ThisTy = method->getThisType(Context);
|
||||
} else if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC)) {
|
||||
// C++0x [expr.prim]p4:
|
||||
// Otherwise, if a member-declarator declares a non-static data member
|
||||
// of a class X, the expression this is a prvalue of type "pointer to X"
|
||||
// within the optional brace-or-equal-initializer.
|
||||
Scope *S = getScopeForContext(DC);
|
||||
if (!S || S->getFlags() & Scope::ThisScope)
|
||||
ThisTy = Context.getPointerType(Context.getRecordType(RD));
|
||||
}
|
||||
|
||||
|
||||
return ThisTy;
|
||||
}
|
||||
|
||||
Sema::CXXThisScopeRAII::CXXThisScopeRAII(Sema &S,
|
||||
Decl *ContextDecl,
|
||||
unsigned CXXThisTypeQuals,
|
||||
bool Enabled)
|
||||
: S(S), OldCXXThisTypeOverride(S.CXXThisTypeOverride), Enabled(false)
|
||||
{
|
||||
if (!Enabled || !ContextDecl)
|
||||
return;
|
||||
|
||||
CXXRecordDecl *Record = 0;
|
||||
if (ClassTemplateDecl *Template = dyn_cast<ClassTemplateDecl>(ContextDecl))
|
||||
Record = Template->getTemplatedDecl();
|
||||
else
|
||||
Record = cast<CXXRecordDecl>(ContextDecl);
|
||||
|
||||
S.CXXThisTypeOverride
|
||||
= S.Context.getPointerType(
|
||||
S.Context.getRecordType(Record).withCVRQualifiers(CXXThisTypeQuals));
|
||||
|
||||
this->Enabled = true;
|
||||
}
|
||||
|
||||
|
||||
Sema::CXXThisScopeRAII::~CXXThisScopeRAII() {
|
||||
if (Enabled) {
|
||||
S.CXXThisTypeOverride = OldCXXThisTypeOverride;
|
||||
}
|
||||
}
|
||||
|
||||
void Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit) {
|
||||
// We don't need to capture this in an unevaluated context.
|
||||
if (ExprEvalContexts.back().Context == Unevaluated && !Explicit)
|
||||
|
@ -739,6 +760,18 @@ ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
|
|||
return Owned(new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit=*/false));
|
||||
}
|
||||
|
||||
bool Sema::isThisOutsideMemberFunctionBody(QualType BaseType) {
|
||||
// If we're outside the body of a member function, then we'll have a specified
|
||||
// type for 'this'.
|
||||
if (CXXThisTypeOverride.isNull())
|
||||
return false;
|
||||
|
||||
// Determine whether we're looking into a class that's currently being
|
||||
// defined.
|
||||
CXXRecordDecl *Class = BaseType->getAsCXXRecordDecl();
|
||||
return Class && Class->isBeingDefined();
|
||||
}
|
||||
|
||||
ExprResult
|
||||
Sema::ActOnCXXTypeConstructExpr(ParsedType TypeRep,
|
||||
SourceLocation LParenLoc,
|
||||
|
@ -4784,8 +4817,13 @@ Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SourceLocation OpLoc,
|
|||
return Owned(Base);
|
||||
}
|
||||
|
||||
// The object type must be complete (or dependent).
|
||||
// The object type must be complete (or dependent), or
|
||||
// C++11 [expr.prim.general]p3:
|
||||
// Unlike the object expression in other contexts, *this is not required to
|
||||
// be of complete type for purposes of class member access (5.2.5) outside
|
||||
// the member function body.
|
||||
if (!BaseType->isDependentType() &&
|
||||
!isThisOutsideMemberFunctionBody(BaseType) &&
|
||||
RequireCompleteType(OpLoc, BaseType,
|
||||
PDiag(diag::err_incomplete_member_access)))
|
||||
return ExprError();
|
||||
|
|
|
@ -101,16 +101,8 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
|
|||
|
||||
DeclContext *DC = SemaRef.getFunctionLevelDeclContext();
|
||||
|
||||
bool isStaticContext =
|
||||
(!isa<CXXMethodDecl>(DC) ||
|
||||
cast<CXXMethodDecl>(DC)->isStatic());
|
||||
|
||||
// C++0x [expr.prim]p4:
|
||||
// Otherwise, if a member-declarator declares a non-static data member
|
||||
// of a class X, the expression this is a prvalue of type "pointer to X"
|
||||
// within the optional brace-or-equal-initializer.
|
||||
if (CurScope->getFlags() & Scope::ThisScope)
|
||||
isStaticContext = false;
|
||||
bool isStaticContext = SemaRef.CXXThisTypeOverride.isNull() &&
|
||||
(!isa<CXXMethodDecl>(DC) || cast<CXXMethodDecl>(DC)->isStatic());
|
||||
|
||||
if (R.isUnresolvableResult())
|
||||
return isStaticContext ? IMA_Unresolved_StaticContext : IMA_Unresolved;
|
||||
|
@ -549,12 +541,13 @@ class RecordMemberExprValidatorCCC : public CorrectionCandidateCallback {
|
|||
}
|
||||
|
||||
static bool
|
||||
LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
|
||||
LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
|
||||
SourceRange BaseRange, const RecordType *RTy,
|
||||
SourceLocation OpLoc, CXXScopeSpec &SS,
|
||||
bool HasTemplateArgs) {
|
||||
RecordDecl *RDecl = RTy->getDecl();
|
||||
if (SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0),
|
||||
if (!SemaRef.isThisOutsideMemberFunctionBody(QualType(RTy, 0)) &&
|
||||
SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0),
|
||||
SemaRef.PDiag(diag::err_typecheck_incomplete_tag)
|
||||
<< BaseRange))
|
||||
return true;
|
||||
|
|
|
@ -2310,23 +2310,42 @@ Sema::SubstituteExplicitTemplateArguments(
|
|||
// explicitly-specified template arguments. If the function has a trailing
|
||||
// return type, substitute it after the arguments to ensure we substitute
|
||||
// in lexical order.
|
||||
if (Proto->hasTrailingReturn() &&
|
||||
SubstParmTypes(Function->getLocation(),
|
||||
Function->param_begin(), Function->getNumParams(),
|
||||
MultiLevelTemplateArgumentList(*ExplicitArgumentList),
|
||||
ParamTypes))
|
||||
return TDK_SubstitutionFailure;
|
||||
|
||||
if (Proto->hasTrailingReturn()) {
|
||||
if (SubstParmTypes(Function->getLocation(),
|
||||
Function->param_begin(), Function->getNumParams(),
|
||||
MultiLevelTemplateArgumentList(*ExplicitArgumentList),
|
||||
ParamTypes))
|
||||
return TDK_SubstitutionFailure;
|
||||
}
|
||||
|
||||
// Instantiate the return type.
|
||||
// FIXME: exception-specifications?
|
||||
QualType ResultType
|
||||
= SubstType(Proto->getResultType(),
|
||||
MultiLevelTemplateArgumentList(*ExplicitArgumentList),
|
||||
Function->getTypeSpecStartLoc(),
|
||||
Function->getDeclName());
|
||||
if (ResultType.isNull() || Trap.hasErrorOccurred())
|
||||
return TDK_SubstitutionFailure;
|
||||
|
||||
QualType ResultType;
|
||||
{
|
||||
// C++11 [expr.prim.general]p3:
|
||||
// If a declaration declares a member function or member function
|
||||
// template of a class X, the expression this is a prvalue of type
|
||||
// "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq
|
||||
// and the end of the function-definition, member-declarator, or
|
||||
// declarator.
|
||||
unsigned ThisTypeQuals = 0;
|
||||
CXXRecordDecl *ThisContext = 0;
|
||||
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Function)) {
|
||||
ThisContext = Method->getParent();
|
||||
ThisTypeQuals = Method->getTypeQualifiers();
|
||||
}
|
||||
|
||||
CXXThisScopeRAII ThisScope(*this, ThisContext, ThisTypeQuals,
|
||||
getLangOpts().CPlusPlus0x);
|
||||
|
||||
ResultType = SubstType(Proto->getResultType(),
|
||||
MultiLevelTemplateArgumentList(*ExplicitArgumentList),
|
||||
Function->getTypeSpecStartLoc(),
|
||||
Function->getDeclName());
|
||||
if (ResultType.isNull() || Trap.hasErrorOccurred())
|
||||
return TDK_SubstitutionFailure;
|
||||
}
|
||||
|
||||
// Instantiate the types of each of the function parameters given the
|
||||
// explicitly-specified template arguments if we didn't do so earlier.
|
||||
if (!Proto->hasTrailingReturn() &&
|
||||
|
|
|
@ -789,6 +789,11 @@ namespace {
|
|||
|
||||
QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
|
||||
FunctionProtoTypeLoc TL);
|
||||
QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
|
||||
FunctionProtoTypeLoc TL,
|
||||
CXXRecordDecl *ThisContext,
|
||||
unsigned ThisTypeQuals);
|
||||
|
||||
ParmVarDecl *TransformFunctionTypeParam(ParmVarDecl *OldParm,
|
||||
int indexAdjustment,
|
||||
llvm::Optional<unsigned> NumExpansions,
|
||||
|
@ -1211,6 +1216,16 @@ QualType TemplateInstantiator::TransformFunctionProtoType(TypeLocBuilder &TLB,
|
|||
return inherited::TransformFunctionProtoType(TLB, TL);
|
||||
}
|
||||
|
||||
QualType TemplateInstantiator::TransformFunctionProtoType(TypeLocBuilder &TLB,
|
||||
FunctionProtoTypeLoc TL,
|
||||
CXXRecordDecl *ThisContext,
|
||||
unsigned ThisTypeQuals) {
|
||||
// We need a local instantiation scope for this function prototype.
|
||||
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
|
||||
return inherited::TransformFunctionProtoType(TLB, TL, ThisContext,
|
||||
ThisTypeQuals);
|
||||
}
|
||||
|
||||
ParmVarDecl *
|
||||
TemplateInstantiator::TransformFunctionTypeParam(ParmVarDecl *OldParm,
|
||||
int indexAdjustment,
|
||||
|
@ -1446,7 +1461,9 @@ static bool NeedsInstantiationAsFunctionType(TypeSourceInfo *T) {
|
|||
TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T,
|
||||
const MultiLevelTemplateArgumentList &Args,
|
||||
SourceLocation Loc,
|
||||
DeclarationName Entity) {
|
||||
DeclarationName Entity,
|
||||
CXXRecordDecl *ThisContext,
|
||||
unsigned ThisTypeQuals) {
|
||||
assert(!ActiveTemplateInstantiations.empty() &&
|
||||
"Cannot perform an instantiation without some context on the "
|
||||
"instantiation stack");
|
||||
|
@ -1461,7 +1478,14 @@ TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T,
|
|||
TypeLoc TL = T->getTypeLoc();
|
||||
TLB.reserve(TL.getFullDataSize());
|
||||
|
||||
QualType Result = Instantiator.TransformType(TLB, TL);
|
||||
QualType Result;
|
||||
|
||||
if (FunctionProtoTypeLoc *Proto = dyn_cast<FunctionProtoTypeLoc>(&TL)) {
|
||||
Result = Instantiator.TransformFunctionProtoType(TLB, *Proto, ThisContext,
|
||||
ThisTypeQuals);
|
||||
} else {
|
||||
Result = Instantiator.TransformType(TLB, TL);
|
||||
}
|
||||
if (Result.isNull())
|
||||
return 0;
|
||||
|
||||
|
@ -1878,24 +1902,33 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
|
|||
CheckCompletedCXXClass(Instantiation);
|
||||
|
||||
// Attach any in-class member initializers now the class is complete.
|
||||
for (unsigned I = 0, N = FieldsWithMemberInitializers.size(); I != N; ++I) {
|
||||
FieldDecl *OldField = FieldsWithMemberInitializers[I].first;
|
||||
FieldDecl *NewField = FieldsWithMemberInitializers[I].second;
|
||||
Expr *OldInit = OldField->getInClassInitializer();
|
||||
{
|
||||
// C++11 [expr.prim.general]p4:
|
||||
// Otherwise, if a member-declarator declares a non-static data member
|
||||
// (9.2) of a class X, the expression this is a prvalue of type "pointer
|
||||
// to X" within the optional brace-or-equal-initializer. It shall not
|
||||
// appear elsewhere in the member-declarator.
|
||||
CXXThisScopeRAII ThisScope(*this, Instantiation, (unsigned)0);
|
||||
|
||||
for (unsigned I = 0, N = FieldsWithMemberInitializers.size(); I != N; ++I) {
|
||||
FieldDecl *OldField = FieldsWithMemberInitializers[I].first;
|
||||
FieldDecl *NewField = FieldsWithMemberInitializers[I].second;
|
||||
Expr *OldInit = OldField->getInClassInitializer();
|
||||
|
||||
ExprResult NewInit = SubstInitializer(OldInit, TemplateArgs,
|
||||
/*CXXDirectInit=*/false);
|
||||
if (NewInit.isInvalid())
|
||||
NewField->setInvalidDecl();
|
||||
else {
|
||||
Expr *Init = NewInit.take();
|
||||
assert(Init && "no-argument initializer in class");
|
||||
assert(!isa<ParenListExpr>(Init) && "call-style init in class");
|
||||
ActOnCXXInClassMemberInitializer(NewField,
|
||||
Init->getSourceRange().getBegin(), Init);
|
||||
ExprResult NewInit = SubstInitializer(OldInit, TemplateArgs,
|
||||
/*CXXDirectInit=*/false);
|
||||
if (NewInit.isInvalid())
|
||||
NewField->setInvalidDecl();
|
||||
else {
|
||||
Expr *Init = NewInit.take();
|
||||
assert(Init && "no-argument initializer in class");
|
||||
assert(!isa<ParenListExpr>(Init) && "call-style init in class");
|
||||
ActOnCXXInClassMemberInitializer(NewField,
|
||||
Init->getSourceRange().getBegin(),
|
||||
Init);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiate late parsed attributes, and attach them to their decls.
|
||||
// See Sema::InstantiateAttrs
|
||||
for (LateInstantiatedAttrVec::iterator I = LateAttrs.begin(),
|
||||
|
|
|
@ -2141,10 +2141,19 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
|
|||
TypeSourceInfo *OldTInfo = D->getTypeSourceInfo();
|
||||
assert(OldTInfo && "substituting function without type source info");
|
||||
assert(Params.empty() && "parameter vector is non-empty at start");
|
||||
|
||||
CXXRecordDecl *ThisContext = 0;
|
||||
unsigned ThisTypeQuals = 0;
|
||||
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
|
||||
ThisContext = Method->getParent();
|
||||
ThisTypeQuals = Method->getTypeQualifiers();
|
||||
}
|
||||
|
||||
TypeSourceInfo *NewTInfo
|
||||
= SemaRef.SubstFunctionDeclType(OldTInfo, TemplateArgs,
|
||||
D->getTypeSpecStartLoc(),
|
||||
D->getDeclName());
|
||||
D->getDeclName(),
|
||||
ThisContext, ThisTypeQuals);
|
||||
if (!NewTInfo)
|
||||
return 0;
|
||||
|
||||
|
@ -2243,6 +2252,22 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New,
|
|||
assert(Proto && "Function template without prototype?");
|
||||
|
||||
if (Proto->hasExceptionSpec() || Proto->getNoReturnAttr()) {
|
||||
// C++11 [expr.prim.general]p3:
|
||||
// If a declaration declares a member function or member function
|
||||
// template of a class X, the expression this is a prvalue of type
|
||||
// "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq
|
||||
// and the end of the function-definition, member-declarator, or
|
||||
// declarator.
|
||||
CXXRecordDecl *ThisContext = 0;
|
||||
unsigned ThisTypeQuals = 0;
|
||||
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(New)) {
|
||||
ThisContext = Method->getParent();
|
||||
ThisTypeQuals = Method->getTypeQualifiers();
|
||||
}
|
||||
Sema::CXXThisScopeRAII ThisScope(SemaRef, ThisContext, ThisTypeQuals,
|
||||
SemaRef.getLangOpts().CPlusPlus0x);
|
||||
|
||||
|
||||
// The function has an exception specification or a "noreturn"
|
||||
// attribute. Substitute into each of the exception types.
|
||||
SmallVector<QualType, 4> Exceptions;
|
||||
|
|
|
@ -522,6 +522,11 @@ public:
|
|||
QualType Transform##CLASS##Type(TypeLocBuilder &TLB, CLASS##TypeLoc T);
|
||||
#include "clang/AST/TypeLocNodes.def"
|
||||
|
||||
QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
|
||||
FunctionProtoTypeLoc TL,
|
||||
CXXRecordDecl *ThisContext,
|
||||
unsigned ThisTypeQuals);
|
||||
|
||||
StmtResult
|
||||
TransformSEHHandler(Stmt *Handler);
|
||||
|
||||
|
@ -4165,6 +4170,15 @@ template<typename Derived>
|
|||
QualType
|
||||
TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB,
|
||||
FunctionProtoTypeLoc TL) {
|
||||
return getDerived().TransformFunctionProtoType(TLB, TL, 0, 0);
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
QualType
|
||||
TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB,
|
||||
FunctionProtoTypeLoc TL,
|
||||
CXXRecordDecl *ThisContext,
|
||||
unsigned ThisTypeQuals) {
|
||||
// Transform the parameters and return type.
|
||||
//
|
||||
// We instantiate in source order, with the return type first followed by
|
||||
|
@ -4189,9 +4203,19 @@ TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB,
|
|||
ParamTypes, &ParamDecls))
|
||||
return QualType();
|
||||
|
||||
ResultType = getDerived().TransformType(TLB, TL.getResultLoc());
|
||||
if (ResultType.isNull())
|
||||
return QualType();
|
||||
{
|
||||
// C++11 [expr.prim.general]p3:
|
||||
// If a declaration declares a member function or member function
|
||||
// template of a class X, the expression this is a prvalue of type
|
||||
// "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq
|
||||
// and the end of the function-definition, member-declarator, or
|
||||
// declarator.
|
||||
Sema::CXXThisScopeRAII ThisScope(SemaRef, ThisContext, ThisTypeQuals);
|
||||
|
||||
ResultType = getDerived().TransformType(TLB, TL.getResultLoc());
|
||||
if (ResultType.isNull())
|
||||
return QualType();
|
||||
}
|
||||
}
|
||||
else {
|
||||
ResultType = getDerived().TransformType(TLB, TL.getResultLoc());
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -verify
|
||||
|
||||
struct A {
|
||||
int &f(int*);
|
||||
float &f(int*) const noexcept;
|
||||
|
||||
int *ptr;
|
||||
auto g1() noexcept(noexcept(f(ptr))) -> decltype(f(this->ptr));
|
||||
auto g2() const noexcept(noexcept(f((*this).ptr))) -> decltype(f(ptr));
|
||||
};
|
||||
|
||||
void testA(A &a) {
|
||||
int &ir = a.g1();
|
||||
float &fr = a.g2();
|
||||
static_assert(!noexcept(a.g1()), "exception-specification failure");
|
||||
static_assert(noexcept(a.g2()), "exception-specification failure");
|
||||
}
|
||||
|
||||
struct B {
|
||||
char g();
|
||||
template<class T> auto f(T t) -> decltype(t + g())
|
||||
{ return t + g(); }
|
||||
};
|
||||
|
||||
template auto B::f(int t) -> decltype(t + g());
|
||||
|
||||
template<typename T>
|
||||
struct C {
|
||||
int &f(T*);
|
||||
float &f(T*) const noexcept;
|
||||
|
||||
T* ptr;
|
||||
auto g1() noexcept(noexcept(f(ptr))) -> decltype(f((*this).ptr));
|
||||
auto g2() const noexcept(noexcept(f(((this))->ptr))) -> decltype(f(ptr));
|
||||
};
|
||||
|
||||
void test_C(C<int> ci) {
|
||||
int *p = 0;
|
||||
int &ir = ci.g1();
|
||||
float &fr = ci.g2();
|
||||
static_assert(!noexcept(ci.g1()), "exception-specification failure");
|
||||
static_assert(noexcept(ci.g2()), "exception-specification failure");
|
||||
}
|
||||
|
||||
namespace PR10036 {
|
||||
template <class I>
|
||||
void
|
||||
iter_swap(I x, I y) noexcept;
|
||||
|
||||
template <class T>
|
||||
class A
|
||||
{
|
||||
T t_;
|
||||
public:
|
||||
void swap(A& a) noexcept(noexcept(iter_swap(&t_, &a.t_)));
|
||||
};
|
||||
|
||||
void test() {
|
||||
A<int> i, j;
|
||||
i.swap(j);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Static {
|
||||
struct X1 {
|
||||
int m;
|
||||
static auto f() -> decltype(m); // expected-error{{'this' cannot be implicitly used in a static member function declaration}}
|
||||
static auto g() -> decltype(this->m); // expected-error{{'this' cannot be used in a static member function declaration}}
|
||||
|
||||
static int h();
|
||||
|
||||
static int i() noexcept(noexcept(m + 2)); // expected-error{{'this' cannot be implicitly used in a static member function declaration}}
|
||||
};
|
||||
|
||||
auto X1::h() -> decltype(m) { return 0; } // expected-error{{'this' cannot be implicitly used in a static member function declaration}}
|
||||
|
||||
template<typename T>
|
||||
struct X2 {
|
||||
int m;
|
||||
|
||||
T f(T*);
|
||||
static T f(int);
|
||||
|
||||
auto g(T x) -> decltype(f(x)) { return 0; }
|
||||
};
|
||||
|
||||
void test_X2() {
|
||||
X2<int>().g(0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 | FileCheck %s
|
||||
|
||||
struct B {
|
||||
template <class U> U f();
|
||||
};
|
||||
|
||||
struct A {
|
||||
B b;
|
||||
// implicitly rewritten to (*this).b.f<U>()
|
||||
template <class U> auto f() -> decltype (b.f<U>());
|
||||
template <class U> auto g() -> decltype (this->b.f<U>());
|
||||
};
|
||||
|
||||
int main() {
|
||||
A a;
|
||||
// CHECK: call i32 @_ZN1A1fIiEEDTcldtdtdefpT1b1fIT_EEEv
|
||||
a.f<int>();
|
||||
// CHECK: call i32 @_ZN1A1gIiEEDTcldtptfpT1b1fIT_EEEv
|
||||
a.g<int>();
|
||||
}
|
|
@ -1255,7 +1255,7 @@ public:
|
|||
void foo4(FooLate *f) __attribute__((exclusive_locks_required(f->mu)));
|
||||
|
||||
static void foo5() __attribute__((exclusive_locks_required(mu))); // \
|
||||
// expected-error {{invalid use of member 'mu' in static member function}}
|
||||
// expected-error {{'this' cannot be implicitly used in a static member function declaration}}
|
||||
|
||||
template <class T>
|
||||
void foo6() __attribute__((exclusive_locks_required(T::statmu))) { }
|
||||
|
|
Loading…
Reference in New Issue