]> granicus.if.org Git - clang/commitdiff
Implement C++11 [expr.prim.general]p3, which permits the use of 'this'
authorDouglas Gregor <dgregor@apple.com>
Mon, 16 Apr 2012 07:05:22 +0000 (07:05 +0000)
committerDouglas Gregor <dgregor@apple.com>
Mon, 16 Apr 2012 07:05:22 +0000 (07:05 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@154799 91177308-0d34-0410-b5e6-96231b3b80d8

20 files changed:
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Scope.h
include/clang/Sema/Sema.h
lib/AST/ItaniumMangle.cpp
lib/AST/StmtProfile.cpp
lib/Parse/ParseCXXInlineMethods.cpp
lib/Parse/ParseDecl.cpp
lib/Parse/ParseExprCXX.cpp
lib/Sema/Sema.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExprCXX.cpp
lib/Sema/SemaExprMember.cpp
lib/Sema/SemaTemplateDeduction.cpp
lib/Sema/SemaTemplateInstantiate.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
lib/Sema/TreeTransform.h
test/CXX/expr/expr.prim/expr.prim.general/p3-0x.cpp [new file with mode: 0644]
test/CodeGenCXX/mangle-this-cxx11.cpp [new file with mode: 0644]
test/SemaCXX/warn-thread-safety-parsing.cpp

index b6b6398da9405f2c92adc12898141aa1f03cd691..48c48722f3b38ff7de7c457426cfac932e441613 100644 (file)
@@ -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<
index e9aa173cb9c783d8ba3247b4262dd20d37ce6389..48f54179ec4a75362d5f00554147306a9cbfadde 100644 (file)
@@ -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
index e8a9ff697169e8d2c8e6298caf412e4eab5eb743..2427a757dc14766d6c38fa23cd96b9d385b1e42e 100644 (file)
@@ -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,
index d7b6354540114dc0f72a05f423af1a1951b114aa..0d405f1f57f21eca4b47bf9d205ab7d18b9d5bb2 100644 (file)
@@ -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;
   }
 }
 
index 4bdded1d600a8766363a992df74364144e08571a..e50523ae860bec4020ea38729c255ca131bb4b92 100644 (file)
@@ -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) {
index c000f69e6e15c4653553be2b962343ea2d9266e4..d2a85355b6f55b4593ea1a32b6d69688f2c06eb9 100644 (file)
@@ -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);
 
index 884820e0238fa00b2df9249ff9779008f73821be..16c90e9b719fcacf64d32702642cacf918f62c36 100644 (file)
@@ -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,
index 491b4a1381ec805a6f43052f3d17c33c7fa46852..ed965115e30b0ce35dc31828dd4c27639df0e66f 100644 (file)
@@ -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());
index fcdfcace0ce8f05d278107352fe3fa002ce7436f..30a9cd751a661d1ed44b61c57a7125975f0a6a67 100644 (file)
@@ -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;
index 8b314b524f47629a2de7771cd48c3ef126190e0e..1d4745dcaa698fe9ec9b825e345c03b5511c7203 100644 (file)
@@ -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);
 }
 
 
index 847f03c948a363184adabff002715b101755d9b8..f488296336cfc2e4b86c2899447e460ecf92ba1d 100644 (file)
@@ -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
index 31a8115f0b484022a5fe1b01a52dfb48ace1475d..e4b40e624cbeb2a9de615d85df76403632916169 100644 (file)
@@ -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();
index 26b88a2a7ee17ffc61b2713e655f7d2910fce4ca..6c84caa3f37121c9139520ef207745e74a99648c 100644 (file)
@@ -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;
index 2ea1e6ff9341d0c7bb96baee6734f9bcf3d8e2ea..d68e4642fc431ea0c1166c058a4303ab290dfa6a 100644 (file)
@@ -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() &&
index 4740145fd5ace624b698783ae39cf84c5d9092e3..60de75b917288324c6c5f87d273592a8b0114655 100644 (file)
@@ -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();
-
-    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);
+  {
+    // 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);
+      }
     }
   }
-
   // Instantiate late parsed attributes, and attach them to their decls.
   // See Sema::InstantiateAttrs
   for (LateInstantiatedAttrVec::iterator I = LateAttrs.begin(),
index 8afe7aca9750f3b1d059184e82abfe416b1cff7e..8d382d1bacae4853a1a9c6fa32347339316bfefe 100644 (file)
@@ -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;
index acfea6e3e4c6939eaf62503c46458976e8bb1f01..5511fc00f559d7849c434ce3e61e4a13053d2dda 100644 (file)
@@ -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());
diff --git a/test/CXX/expr/expr.prim/expr.prim.general/p3-0x.cpp b/test/CXX/expr/expr.prim/expr.prim.general/p3-0x.cpp
new file mode 100644 (file)
index 0000000..f00e358
--- /dev/null
@@ -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);
+  }
+}
diff --git a/test/CodeGenCXX/mangle-this-cxx11.cpp b/test/CodeGenCXX/mangle-this-cxx11.cpp
new file mode 100644 (file)
index 0000000..f9e9479
--- /dev/null
@@ -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>();
+}
index 587cb8a4c729ecaca77922d703b6360ff3eaaf3e..c2fa1d77a01a41f32279fdf3e561d91ae6f0d53a 100644 (file)
@@ -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))) { }