rsmith created this revision.
rsmith added a reviewer: aaron.ballman.
Herald added a reviewer: javed.absar.
Herald added a subscriber: kristof.beyls.

This patch adds support for a new attribute, [[clang::lifetimebound]], that 
indicates that the lifetime of a function result is related to one of the 
function arguments. When walking an initializer to make sure that the lifetime 
of the initial value is at least as long as the lifetime of the initialized 
object, we step through parameters (including the implicit object parameter of 
a non-static member function) that are marked with this attribute.

There's nowhere to write an attribute on the implicit object parameter, so in 
lieu of that, it may be applied to a function type (where it appears 
immediately after the cv-qualifiers and ref-qualifier, which is as close to a 
declaration of the implicit object parameter as we have). I'm currently 
modeling this in the AST as the attribute appertaining to a `ParmVarDecl` or to 
a function type, but in the latter case I'd be happy to stash it somewhere else 
if there is a better home for it.


Repository:
  rC Clang

https://reviews.llvm.org/D49922

Files:
  include/clang/AST/Type.h
  include/clang/AST/TypeLoc.h
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/AST/Type.cpp
  lib/AST/TypePrinter.cpp
  lib/Parse/ParseDeclCXX.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaDeclAttr.cpp
  lib/Sema/SemaInit.cpp
  lib/Sema/SemaOverload.cpp
  lib/Sema/SemaType.cpp
  utils/TableGen/ClangAttrEmitter.cpp

Index: utils/TableGen/ClangAttrEmitter.cpp
===================================================================
--- utils/TableGen/ClangAttrEmitter.cpp
+++ utils/TableGen/ClangAttrEmitter.cpp
@@ -3305,11 +3305,16 @@
   // Otherwise, generate an appertainsTo check specific to this attribute which
   // checks all of the given subjects against the Decl passed in. Return the
   // name of that check to the caller.
+  //
+  // If D is null, that means the attribute was not applied to a declaration
+  // at all (for instance because it was applied to a type), or that the caller
+  // has determined that the check should fail (perhaps prior to the creation
+  // of the declaration).
   std::string FnName = "check" + Attr.getName().str() + "AppertainsTo";
   std::stringstream SS;
   SS << "static bool " << FnName << "(Sema &S, const ParsedAttr &Attr, ";
   SS << "const Decl *D) {\n";
-  SS << "  if (";
+  SS << "  if (!D || (";
   for (auto I = Subjects.begin(), E = Subjects.end(); I != E; ++I) {
     // If the subject has custom code associated with it, generate a function
     // for it. The function cannot be inlined into this check (yet) because it
@@ -3325,7 +3330,7 @@
     if (I + 1 != E)
       SS << " && ";
   }
-  SS << ") {\n";
+  SS << ")) {\n";
   SS << "    S.Diag(Attr.getLoc(), diag::";
   SS << (Warn ? "warn_attribute_wrong_decl_type_str" :
                "err_attribute_wrong_decl_type_str");
Index: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp
+++ lib/Sema/SemaType.cpp
@@ -5233,6 +5233,8 @@
     return ParsedAttr::AT_ObjCKindOf;
   case AttributedType::attr_ns_returns_retained:
     return ParsedAttr::AT_NSReturnsRetained;
+  case AttributedType::attr_lifetimebound:
+    return ParsedAttr::AT_LifetimeBound;
   }
   llvm_unreachable("unexpected attribute kind!");
 }
@@ -7194,6 +7196,18 @@
   T = State.getSema().Context.getAddrSpaceQualType(T, ImpAddr);
 }
 
+static void HandleLifetimeBoundAttr(QualType &CurType,
+                                    const ParsedAttr &Attr,
+                                    Sema &S, Declarator &D) {
+  if (D.isDeclarationOfFunction()) {
+    CurType = S.Context.getAttributedType(AttributedType::attr_lifetimebound,
+                                          CurType, CurType);
+  } else {
+    Attr.diagnoseAppertainsTo(S, nullptr);
+  }
+}
+
+
 static void processTypeAttrs(TypeProcessingState &state, QualType &type,
                              TypeAttrLocation TAL,
                              ParsedAttributesView &attrs) {
@@ -7298,6 +7312,13 @@
       HandleOpenCLAccessAttr(type, attr, state.getSema());
       attr.setUsedAsTypeAttr();
       break;
+    case ParsedAttr::AT_LifetimeBound:
+      if (TAL == TAL_DeclChunk) {
+        HandleLifetimeBoundAttr(type, attr, state.getSema(),
+                                state.getDeclarator());
+        attr.setUsedAsTypeAttr();
+      }
+      break;
 
     MS_TYPE_ATTRS_CASELIST:
       if (!handleMSPointerTypeQualifierAttr(state, attr, type))
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -5230,7 +5230,9 @@
   if (!Context.hasSameType(From->getType(), DestType))
     From = ImpCastExprToType(From, DestType, CK_NoOp,
                              From->getValueKind()).get();
-  return From;
+  if (From->getType()->isPointerType())
+    return From;
+  return TemporaryMaterializationConversion(From);
 }
 
 /// TryContextuallyConvertToBool - Attempt to contextually convert the
Index: lib/Sema/SemaInit.cpp
===================================================================
--- lib/Sema/SemaInit.cpp
+++ lib/Sema/SemaInit.cpp
@@ -6322,6 +6322,7 @@
     AddressOf,
     VarInit,
     LValToRVal,
+    LifetimeBoundCall,
   } Kind;
   Expr *E;
   Decl *D = nullptr;
@@ -6361,6 +6362,68 @@
                                              Expr *Init, LocalVisitor Visit,
                                              bool RevisitSubinits);
 
+static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
+                                                  Expr *Init, ReferenceKind RK,
+                                                  LocalVisitor Visit);
+
+static bool implicitObjectParamIsLifetimeBound(FunctionDecl *FD) {
+  TypeSourceInfo *TSI = FD->getTypeSourceInfo();
+  if (!TSI)
+    return false;
+  for (TypeLoc TL = TSI->getTypeLoc();
+       auto ATL = TL.getAsAdjusted<AttributedTypeLoc>();
+       TL = ATL.getModifiedLoc()) {
+    if (ATL.getAttrKind() == AttributedType::attr_lifetimebound)
+      return true;
+  }
+  return false;
+}
+
+static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call,
+                                        LocalVisitor Visit) {
+  FunctionDecl *Callee;
+  ArrayRef<Expr*> Args;
+
+  if (auto *CE = dyn_cast<CallExpr>(Call)) {
+    Callee = CE->getDirectCallee();
+    Args = llvm::makeArrayRef(CE->getArgs(), CE->getNumArgs());
+  } else {
+    auto *CCE = cast<CXXConstructExpr>(Call);
+    Callee = CCE->getConstructor();
+    Args = llvm::makeArrayRef(CCE->getArgs(), CCE->getNumArgs());
+  }
+  if (!Callee)
+    return;
+
+  Expr *ObjectArg = nullptr;
+  if (isa<CXXOperatorCallExpr>(Call) && Callee->isCXXInstanceMember()) {
+    ObjectArg = Args[0];
+    Args = Args.slice(1);
+  } else if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {
+    ObjectArg = MCE->getImplicitObjectArgument();
+  }
+
+  auto VisitLifetimeBoundArg = [&](Decl *D, Expr *Arg) {
+    Path.push_back({IndirectLocalPathEntry::LifetimeBoundCall, Arg, D});
+    if (Arg->isGLValue())
+      visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding,
+                                            Visit);
+    else
+      visitLocalsRetainedByInitializer(Path, Arg, Visit, true);
+    Path.pop_back();
+  };
+
+  if (ObjectArg && implicitObjectParamIsLifetimeBound(Callee))
+    VisitLifetimeBoundArg(Callee, ObjectArg);
+
+  for (unsigned I = 0,
+                N = std::min<unsigned>(Callee->getNumParams(), Args.size());
+       I != N; ++I) {
+    if (Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
+      VisitLifetimeBoundArg(Callee->getParamDecl(I), Args[I]);
+  }
+}
+
 /// Visit the locals that would be reachable through a reference bound to the
 /// glvalue expression \c Init.
 static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
@@ -6420,6 +6483,9 @@
                                        true);
   }
 
+  if (isa<CallExpr>(Init))
+    return visitLifetimeBoundArguments(Path, Init, Visit);
+
   switch (Init->getStmtClass()) {
   case Stmt::DeclRefExprClass: {
     // If we find the name of a local non-reference parameter, we could have a
@@ -6483,22 +6549,90 @@
                                              bool RevisitSubinits) {
   RevertToOldSizeRAII RAII(Path);
 
-  // Step into CXXDefaultInitExprs so we can diagnose cases where a
-  // constructor inherits one as an implicit mem-initializer.
-  if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) {
-    Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()});
-    Init = DIE->getExpr();
-  }
+  Expr *Old;
+  do {
+    Old = Init;
 
-  if (auto *EWC = dyn_cast<ExprWithCleanups>(Init))
-    Init = EWC->getSubExpr();
+    // Step into CXXDefaultInitExprs so we can diagnose cases where a
+    // constructor inherits one as an implicit mem-initializer.
+    if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) {
+      Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()});
+      Init = DIE->getExpr();
+    }
 
-  // Dig out the expression which constructs the extended temporary.
-  Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments());
+    if (auto *EWC = dyn_cast<ExprWithCleanups>(Init))
+      Init = EWC->getSubExpr();
 
-  if (CXXBindTemporaryExpr *BTE = dyn_cast<CXXBindTemporaryExpr>(Init))
-    Init = BTE->getSubExpr();
+    // Dig out the expression which constructs the extended temporary.
+    Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments());
 
+    if (CXXBindTemporaryExpr *BTE = dyn_cast<CXXBindTemporaryExpr>(Init))
+      Init = BTE->getSubExpr();
+
+    Init = Init->IgnoreParens();
+
+    // Step over value-preserving rvalue casts.
+    if (auto *CE = dyn_cast<CastExpr>(Init)) {
+      switch (CE->getCastKind()) {
+      case CK_LValueToRValue:
+        // If we can match the lvalue to a const object, we can look at its
+        // initializer.
+        Path.push_back({IndirectLocalPathEntry::LValToRVal, CE});
+        return visitLocalsRetainedByReferenceBinding(
+            Path, Init, RK_ReferenceBinding,
+            [&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool {
+          if (auto *DRE = dyn_cast<DeclRefExpr>(L)) {
+            auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
+            if (VD && VD->getType().isConstQualified() && VD->getInit()) {
+              Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD});
+              visitLocalsRetainedByInitializer(Path, VD->getInit(), Visit, true);
+            }
+          } else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) {
+            if (MTE->getType().isConstQualified())
+              visitLocalsRetainedByInitializer(Path, MTE->GetTemporaryExpr(),
+                                               Visit, true);
+          }
+          return false;
+        });
+
+        // We assume that objects can be retained by pointers cast to integers,
+        // but not if the integer is cast to floating-point type or to _Complex.
+        // We assume that casts to 'bool' do not preserve enough information to
+        // retain a local object.
+      case CK_NoOp:
+      case CK_BitCast:
+      case CK_BaseToDerived:
+      case CK_DerivedToBase:
+      case CK_UncheckedDerivedToBase:
+      case CK_Dynamic:
+      case CK_ToUnion:
+      case CK_UserDefinedConversion:
+      case CK_ConstructorConversion:
+      case CK_IntegralToPointer:
+      case CK_PointerToIntegral:
+      case CK_VectorSplat:
+      case CK_IntegralCast:
+      case CK_CPointerToObjCPointerCast:
+      case CK_BlockPointerToObjCPointerCast:
+      case CK_AnyPointerToBlockPointerCast:
+      case CK_AddressSpaceConversion:
+        break;
+
+      case CK_ArrayToPointerDecay:
+        // Model array-to-pointer decay as taking the address of the array
+        // lvalue.
+        Path.push_back({IndirectLocalPathEntry::AddressOf, CE});
+        return visitLocalsRetainedByReferenceBinding(Path, CE->getSubExpr(),
+                                                     RK_ReferenceBinding, Visit);
+
+      default:
+        return;
+      }
+
+      Init = CE->getSubExpr();
+    }
+  } while (Old != Init);
+
   // C++17 [dcl.init.list]p6:
   //   initializing an initializer_list object from the array extends the
   //   lifetime of the array exactly like binding a reference to a temporary.
@@ -6558,66 +6692,9 @@
     return;
   }
 
-  // Step over value-preserving rvalue casts.
-  while (auto *CE = dyn_cast<CastExpr>(Init)) {
-    switch (CE->getCastKind()) {
-    case CK_LValueToRValue:
-      // If we can match the lvalue to a const object, we can look at its
-      // initializer.
-      Path.push_back({IndirectLocalPathEntry::LValToRVal, CE});
-      return visitLocalsRetainedByReferenceBinding(
-          Path, Init, RK_ReferenceBinding,
-          [&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool {
-        if (auto *DRE = dyn_cast<DeclRefExpr>(L)) {
-          auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
-          if (VD && VD->getType().isConstQualified() && VD->getInit()) {
-            Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD});
-            visitLocalsRetainedByInitializer(Path, VD->getInit(), Visit, true);
-          }
-        } else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) {
-          if (MTE->getType().isConstQualified())
-            visitLocalsRetainedByInitializer(Path, MTE->GetTemporaryExpr(),
-                                             Visit, true);
-        }
-        return false;
-      });
+  if (isa<CallExpr>(Init) || isa<CXXConstructExpr>(Init))
+    return visitLifetimeBoundArguments(Path, Init, Visit);
 
-      // We assume that objects can be retained by pointers cast to integers,
-      // but not if the integer is cast to floating-point type or to _Complex.
-      // We assume that casts to 'bool' do not preserve enough information to
-      // retain a local object.
-    case CK_NoOp:
-    case CK_BitCast:
-    case CK_BaseToDerived:
-    case CK_DerivedToBase:
-    case CK_UncheckedDerivedToBase:
-    case CK_Dynamic:
-    case CK_ToUnion:
-    case CK_IntegralToPointer:
-    case CK_PointerToIntegral:
-    case CK_VectorSplat:
-    case CK_IntegralCast:
-    case CK_CPointerToObjCPointerCast:
-    case CK_BlockPointerToObjCPointerCast:
-    case CK_AnyPointerToBlockPointerCast:
-    case CK_AddressSpaceConversion:
-      break;
-
-    case CK_ArrayToPointerDecay:
-      // Model array-to-pointer decay as taking the address of the array
-      // lvalue.
-      Path.push_back({IndirectLocalPathEntry::AddressOf, CE});
-      return visitLocalsRetainedByReferenceBinding(Path, CE->getSubExpr(),
-                                                   RK_ReferenceBinding, Visit);
-
-    default:
-      return;
-    }
-
-    Init = CE->getSubExpr();
-  }
-
-  Init = Init->IgnoreParens();
   switch (Init->getStmtClass()) {
   case Stmt::UnaryOperatorClass: {
     auto *UO = cast<UnaryOperator>(Init);
@@ -6697,6 +6774,7 @@
     switch (Path[I].Kind) {
     case IndirectLocalPathEntry::AddressOf:
     case IndirectLocalPathEntry::LValToRVal:
+    case IndirectLocalPathEntry::LifetimeBoundCall:
       // These exist primarily to mark the path as not permitting or
       // supporting lifetime extension.
       break;
@@ -6875,6 +6953,10 @@
         // supporting lifetime extension.
         break;
 
+      case IndirectLocalPathEntry::LifetimeBoundCall:
+        // FIXME: Consider adding a note for this.
+        break;
+
       case IndirectLocalPathEntry::DefaultInit: {
         auto *FD = cast<FieldDecl>(Elem.D);
         Diag(FD->getLocation(), diag::note_init_with_default_member_initalizer)
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -6150,6 +6150,9 @@
   case ParsedAttr::AT_Restrict:
     handleRestrictAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_LifetimeBound:
+    handleSimpleAttribute<LifetimeBoundAttr>(S, D, AL);
+    break;
   case ParsedAttr::AT_MayAlias:
     handleSimpleAttribute<MayAliasAttr>(S, D, AL);
     break;
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -6008,6 +6008,27 @@
             << Attr;
         ND.dropAttr<NotTailCalledAttr>();
       }
+
+  // Check the attributes on the function type, if any.
+  if (auto *FD = dyn_cast<FunctionDecl>(&ND)) {
+    for (TypeLoc TL = FD->getTypeSourceInfo()->getTypeLoc();
+         auto ATL = TL.getAsAdjusted<AttributedTypeLoc>();
+         TL = ATL.getModifiedLoc()) {
+      // The [[lifetimebound]] attribute can be applied to the implicit object
+      // parameter of a non-static member function (other than a dtor) by
+      // applying it to the function type.
+      if (ATL.getAttrKind() == AttributedType::attr_lifetimebound) {
+        auto *MD = dyn_cast<CXXMethodDecl>(FD);
+        if (!MD || MD->isStatic()) {
+          S.Diag(ATL.getAttrNameLoc(), diag::err_lifetimebound_no_object_param)
+              << !MD << ATL.getLocalSourceRange();
+        } else if (isa<CXXConstructorDecl>(MD) || isa<CXXDestructorDecl>(MD)) {
+          S.Diag(ATL.getAttrNameLoc(), diag::err_lifetimebound_ctor_dtor)
+              << isa<CXXDestructorDecl>(MD) << ATL.getLocalSourceRange();
+        }
+      }
+    }
+  }
 }
 
 static void checkDLLAttributeRedeclaration(Sema &S, NamedDecl *OldDecl,
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp
+++ lib/Parse/ParseDeclCXX.cpp
@@ -3808,6 +3808,7 @@
   case ParsedAttr::AT_Deprecated:
   case ParsedAttr::AT_FallThrough:
   case ParsedAttr::AT_CXX11NoReturn:
+  case ParsedAttr::AT_LifetimeBound:
     return true;
   case ParsedAttr::AT_WarnUnusedResult:
     return !ScopeName && AttrName->getName().equals("nodiscard");
Index: lib/AST/TypePrinter.cpp
===================================================================
--- lib/AST/TypePrinter.cpp
+++ lib/AST/TypePrinter.cpp
@@ -1443,9 +1443,27 @@
     return;
   }
 
+  if (T->getAttrKind() == AttributedType::attr_lifetimebound) {
+    OS << " [[clang::lifetimebound]]";
+    return;
+  }
+
   OS << " __attribute__((";
   switch (T->getAttrKind()) {
-  default: llvm_unreachable("This attribute should have been handled already");
+  case AttributedType::attr_lifetimebound:
+  case AttributedType::attr_nonnull:
+  case AttributedType::attr_nullable:
+  case AttributedType::attr_null_unspecified:
+  case AttributedType::attr_objc_gc:
+  case AttributedType::attr_objc_inert_unsafe_unretained:
+  case AttributedType::attr_objc_kindof:
+  case AttributedType::attr_objc_ownership:
+  case AttributedType::attr_ptr32:
+  case AttributedType::attr_ptr64:
+  case AttributedType::attr_sptr:
+  case AttributedType::attr_uptr:
+    llvm_unreachable("This attribute should have been handled already");
+
   case AttributedType::attr_address_space:
     OS << "address_space(";
     // FIXME: printing the raw LangAS value is wrong. This should probably
@@ -1489,36 +1507,6 @@
     break;
   }
 
-  case AttributedType::attr_objc_gc: {
-    OS << "objc_gc(";
-
-    QualType tmp = T->getEquivalentType();
-    while (tmp.getObjCGCAttr() == Qualifiers::GCNone) {
-      QualType next = tmp->getPointeeType();
-      if (next == tmp) break;
-      tmp = next;
-    }
-
-    if (tmp.isObjCGCWeak())
-      OS << "weak";
-    else
-      OS << "strong";
-    OS << ')';
-    break;
-  }
-
-  case AttributedType::attr_objc_ownership:
-    OS << "objc_ownership(";
-    switch (T->getEquivalentType().getObjCLifetime()) {
-    case Qualifiers::OCL_None: llvm_unreachable("no ownership!");
-    case Qualifiers::OCL_ExplicitNone: OS << "none"; break;
-    case Qualifiers::OCL_Strong: OS << "strong"; break;
-    case Qualifiers::OCL_Weak: OS << "weak"; break;
-    case Qualifiers::OCL_Autoreleasing: OS << "autoreleasing"; break;
-    }
-    OS << ')';
-    break;
-
   case AttributedType::attr_ns_returns_retained:
     OS << "ns_returns_retained";
     break;
Index: lib/AST/Type.cpp
===================================================================
--- lib/AST/Type.cpp
+++ lib/AST/Type.cpp
@@ -3174,6 +3174,7 @@
   case AttributedType::attr_nonnull:
   case AttributedType::attr_nullable:
   case AttributedType::attr_null_unspecified:
+  case AttributedType::attr_lifetimebound:
     return true;
 
   // These aren't qualifiers; they rewrite the modified type to be a
@@ -3243,6 +3244,7 @@
   case attr_null_unspecified:
   case attr_objc_kindof:
   case attr_nocf_check:
+  case attr_lifetimebound:
     return false;
 
   case attr_pcs:
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -7851,6 +7851,13 @@
   "null returned from %select{function|method}0 that requires a non-null return value">,
   InGroup<NonNull>;
 
+def err_lifetimebound_no_object_param : Error<
+  "'lifetimebound' attribute cannot be applied; %select{static |non-}0member "
+  "function has no implicit object parameter">;
+def err_lifetimebound_ctor_dtor : Error<
+  "'lifetimebound' attribute cannot be applied to a "
+  "%select{constructor|destructor}0">;
+
 // CHECK: returning address/reference of stack memory
 def warn_ret_stack_addr_ref : Warning<
   "%select{address of|reference to}0 stack memory associated with "
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -2362,6 +2362,22 @@
   }];
 }
 
+def LifetimeBoundDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+The ``lifetime_bound`` attribute indicates that a resource owned by
+a function parameter or implicit object parameter
+is retained by the return value of the annotated function
+(or, for a constructor, in the value of the constructed object).
+It is only supported in C++.
+
+This attribute provides an experimental implementation of the facility
+described in the C++ committee paper [http://wg21.link/p0936r0](P0936R0),
+and is subject to change as the design of the corresponding functionality
+changes.
+  }];
+}
+
 def TrivialABIDocs : Documentation {
   let Category = DocCatVariable;
   let Content = [{
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -141,6 +141,13 @@
                                        isa<BlockDecl>(S)}],
                                      "non-K&R-style functions">;
 
+// A subject that matches the implicit object parameter of a non-static member
+// function. Accepted as a function type attribute on the type of such a
+// member function.
+// FIXME: This does not actually ever match currently.
+def ImplicitObjectParameter : SubsetSubject<Function, [{false}],
+                                            "implicit object parameters">;
+
 // A single argument to an attribute
 class Argument<string name, bit optional, bit fake = 0> {
   string Name = name;
@@ -1211,6 +1218,13 @@
   let Documentation = [LayoutVersionDocs];
 }
 
+def LifetimeBound : InheritableAttr {
+  let Spellings = [Clang<"lifetimebound", 0>];
+  let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>;
+  let Documentation = [LifetimeBoundDocs];
+  let LangOpts = [CPlusPlus];
+}
+
 def TrivialABI : InheritableAttr {
   // This attribute does not have a C [[]] spelling because it requires the
   // CPlusPlus language option.
Index: include/clang/AST/TypeLoc.h
===================================================================
--- include/clang/AST/TypeLoc.h
+++ include/clang/AST/TypeLoc.h
@@ -93,8 +93,8 @@
   }
 
   /// Convert to the specified TypeLoc type, returning a null TypeLoc if
-  /// this TypeLock is not of the desired type. It will consider type
-  /// adjustments from a type that wad written as a T to another type that is
+  /// this TypeLoc is not of the desired type. It will consider type
+  /// adjustments from a type that was written as a T to another type that is
   /// still canonically a T (ignores parens, attributes, elaborated types, etc).
   template <typename T>
   T getAsAdjusted() const;
Index: include/clang/AST/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -4234,6 +4234,7 @@
     attr_null_unspecified,
     attr_objc_kindof,
     attr_objc_inert_unsafe_unretained,
+    attr_lifetimebound,
   };
 
 private:
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D49922: [... Richard Smith - zygoloid via Phabricator via cfe-commits

Reply via email to