xazax.hun created this revision.
xazax.hun added reviewers: mgehre, ymandel, gribozavr2, aaron.ballman, rsmith, 
rjmccall.
xazax.hun added a project: clang.
Herald added subscribers: Szelethus, Charusso, gamesh411, dkrupp, rnkovacs, 
mgorny.

This patch corresponds to this RFC: 
http://lists.llvm.org/pipermail/cfe-dev/2019-December/064067.html

This does not cover everything yet, but I wanted to keep the patch small to be 
incremental.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D72810

Files:
  clang/include/clang/AST/Attr.h
  clang/include/clang/AST/LifetimeAttr.h
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/DiagnosticGroups.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/AST/CMakeLists.txt
  clang/lib/AST/LifetimeAttr.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaInit.cpp
  clang/lib/Sema/SemaType.cpp
  clang/test/Sema/attr-psets-annotation.cpp

Index: clang/test/Sema/attr-psets-annotation.cpp
===================================================================
--- /dev/null
+++ clang/test/Sema/attr-psets-annotation.cpp
@@ -0,0 +1,134 @@
+// RUN: %clang_cc1 -fsyntax-only -Wlifetime -Wlifetime-dump-contracts -verify %s
+
+namespace gsl {
+struct null_t {
+  template <typename T>
+  operator T() const;
+} Null;
+struct static_t {
+  template <typename T>
+  operator T() const;
+} Static;
+struct invalid_t {
+  template <typename T>
+  operator T() const;
+} Invalid;
+
+struct return_t {
+  template <typename T>
+  operator T() const;
+} Return;
+
+template <typename T>
+struct PointerTraits {
+  static auto deref(const T &t) -> decltype(*t);
+};
+
+template <typename T>
+struct PointerTraits<T *> {
+  static const T &deref(const T *t);
+};
+
+template <typename T>
+struct PointerTraits<T &> {
+  static const T &deref(const T &t);
+};
+
+struct PSet {
+  PSet(...);
+};
+
+template <typename T>
+auto deref(const T &t) -> decltype(PointerTraits<T>::deref(t));
+
+template <typename T>
+bool lifetime(const T &lhs, const PSet &rhs);
+} // namespace gsl
+
+using namespace gsl;
+
+void basic(int *a, int *b) [[gsl::pre(lifetime(b, {a}))]];
+// expected-warning@-1 {{Pre { b -> { a }; }}}
+
+void specials(int *a, int *b, int *c)
+    [[gsl::pre(lifetime(a, {Null}))]]
+    [[gsl::pre(lifetime(b, {Static}))]]
+    [[gsl::pre(lifetime(c, {Invalid}))]];
+// expected-warning@-4 {{Pre { a -> { Null }; b -> { Static }; c -> { Invalid }; }}}
+
+void variadic(int *a, int *b, int *c)
+    [[gsl::pre(lifetime(b, {a, c}))]];
+// expected-warning@-2 {{Pre { b -> { a c }; }}}
+
+void variadic_special(int *a, int *b, int *c)
+    [[gsl::pre(lifetime(b, {a, Null}))]];
+// expected-warning@-2 {{Pre { b -> { Null a }; }}}
+
+void multiple_annotations(int *a, int *b, int *c)
+    [[gsl::pre(lifetime(b, {a}))]]
+    [[gsl::pre(lifetime(c, {a}))]];
+// expected-warning@-3 {{Pre { b -> { a }; c -> { a }; }}}
+
+void multiple_annotations_chained(int *a, int *b, int *c)
+    [[gsl::pre(lifetime(b, {a}))]]
+    [[gsl::pre(lifetime(c, {b}))]];
+// expected-warning@-3 {{Pre { b -> { a }; c -> { a }; }}}
+
+void deref_ptr(int *a, int *b, int **c)
+    [[gsl::pre(lifetime(deref(c), {a}))]];
+// expected-warning@-2 {{Pre { *c -> { a }; }}}
+
+void deref_ptr_pointee(int *a, int *b, int **c)
+    [[gsl::pre(lifetime(a, {deref(c)}))]];
+// expected-warning@-2 {{Pre { a -> { *c }; }}}
+
+void deref_ref(int *a, int *b, int *&c)
+    [[gsl::pre(lifetime(deref(c), {a}))]];
+// expected-warning@-2 {{Pre { *c -> { a }; }}}
+
+void deref_ref_pointee(int *a, int *b, int *&c)
+    [[gsl::pre(lifetime(a, {deref(c)}))]];
+// expected-warning@-2 {{Pre { a -> { *c }; }}}
+
+struct [[gsl::Owner(void)]] X {
+  void f(X **out)
+      [[gsl::post(lifetime(deref(out), {this}))]];
+  // expected-warning@-2 {{Pre { }  Post { *out -> { this }; }}}
+  X *operator+(const X& other)
+      [[gsl::post(lifetime(Return, {other}))]];
+  // expected-warning@-2 {{Pre { }  Post { (return value) -> { other }; }}}
+};
+
+template <typename It, typename T>
+It find(It begin, It end, const T &val)
+    [[gsl::pre(lifetime(end, {begin}))]]
+    [[gsl::post(lifetime(Return, {begin}))]];
+// expected-warning@-3 {{Pre { end -> { begin }; }  Post { (return value) -> { begin }; }}}
+
+int *find_nontemp(int *begin, int *end, const int &val)
+    [[gsl::pre(lifetime(end, {begin}))]]
+    [[gsl::post(lifetime(Return, {begin}))]];
+// expected-warning@-3 {{Pre { end -> { begin }; }  Post { (return value) -> { begin }; }}}
+
+struct [[gsl::Owner(int)]] MyOwner {
+  int *begin()
+      [[gsl::post(lifetime(Return, {this}))]];
+  // expected-warning@-2 {{Pre { }  Post { (return value) -> { this }; }}}
+  int *end()
+      [[gsl::post(lifetime(Return, {this}))]];
+  // expected-warning@-2 {{Pre { }  Post { (return value) -> { this }; }}}
+};
+
+void testGslWarning() {
+  int *res = find(MyOwner{}.begin(), MyOwner{}.end(), 5);
+  // expected-warning@-1 {{object backing the pointer will be destroyed at the end of the full-expression}}
+  (void)res;
+  int *res2 = find_nontemp(MyOwner{}.begin(), MyOwner{}.end(), 5);
+  // expected-warning@-1 {{object backing the pointer will be destroyed at the end of the full-expression}}
+  (void)res2;
+  X x;
+  // TODO: this should work without X annotated as owner.
+  X *xp = x + X{};
+  // expected-warning@-1 {{object backing the pointer will be destroyed at the end of the full-expression}}
+  (void)xp;
+}
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -7552,6 +7552,12 @@
         HandleLifetimeBoundAttr(state, type, attr);
       break;
 
+    // Move function type attribute to the declarator.
+    case ParsedAttr::AT_LifetimeContract:
+      moveAttrFromListToList(attr, state.getCurrentAttributes(),
+                             state.getDeclarator().getAttributes());
+      break;
+
     case ParsedAttr::AT_NoDeref: {
       ASTContext &Ctx = state.getSema().Context;
       type = state.getAttributedType(createSimpleAttr<NoDerefAttr>(Ctx, attr),
Index: clang/lib/Sema/SemaInit.cpp
===================================================================
--- clang/lib/Sema/SemaInit.cpp
+++ clang/lib/Sema/SemaInit.cpp
@@ -15,6 +15,7 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/ExprOpenMP.h"
+#include "clang/AST/LifetimeAttr.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/CharInfo.h"
 #include "clang/Basic/TargetInfo.h"
@@ -6778,6 +6779,57 @@
   return false;
 }
 
+static bool shouldTrackContract(const LifetimeContractAttr *LCAttr,
+                                const FunctionDecl *FD, ContractVariable CV) {
+  if (!LCAttr || !LCAttr->PostContracts)
+    return false;
+  const LifetimeContractMap &PM = *LCAttr->PostContracts;
+  auto It = PM.find(ContractVariable::returnVal());
+  if (It == PM.end())
+    return false;
+  return It->second.count(CV);
+}
+
+static LifetimeContractAttr *getLifetimeAttr(const FunctionDecl *FD) {
+  if (const auto LCAttr = FD->getAttr<LifetimeContractAttr>()) {
+    // The actual information is stored at primary templates for
+    // specializations.
+    if (const auto *FTD = FD->getPrimaryTemplate()) {
+      assert(FTD->getTemplatedDecl()->hasAttr<LifetimeContractAttr>());
+      return FTD->getTemplatedDecl()->getAttr<LifetimeContractAttr>();
+    }
+    return LCAttr;
+  }
+  return nullptr;
+}
+
+namespace {
+struct CallInfo {
+  FunctionDecl *Callee = nullptr;
+  ArrayRef<Expr *> Args;
+  Expr *ObjectArg = nullptr;
+};
+} // namespace
+
+static CallInfo getCallInfo(Expr *Call) {
+  CallInfo Info;
+  if (auto *CE = dyn_cast<CallExpr>(Call)) {
+    Info.Callee = CE->getDirectCallee();
+    Info.Args = llvm::makeArrayRef(CE->getArgs(), CE->getNumArgs());
+  } else {
+    auto *CCE = cast<CXXConstructExpr>(Call);
+    Info.Callee = CCE->getConstructor();
+    Info.Args = llvm::makeArrayRef(CCE->getArgs(), CCE->getNumArgs());
+  }
+  if (isa<CXXOperatorCallExpr>(Call) && Info.Callee->isCXXInstanceMember()) {
+    Info.ObjectArg = Info.Args[0];
+    Info.Args = Info.Args.slice(1);
+  } else if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {
+    Info.ObjectArg = MCE->getImplicitObjectArgument();
+  }
+  return Info;
+}
+
 static void handleGslAnnotatedTypes(IndirectLocalPath &Path, Expr *Call,
                                     LocalVisitor Visit) {
   auto VisitPointerArg = [&](const Decl *D, Expr *Arg, bool Value) {
@@ -6808,32 +6860,35 @@
     Path.pop_back();
   };
 
-  if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {
-    const auto *MD = cast_or_null<CXXMethodDecl>(MCE->getDirectCallee());
-    if (MD && shouldTrackImplicitObjectArg(MD))
-      VisitPointerArg(MD, MCE->getImplicitObjectArgument(),
-                      !MD->getReturnType()->isReferenceType());
+  CallInfo CI = getCallInfo(Call);
+  if (!CI.Callee)
     return;
-  } else if (auto *OCE = dyn_cast<CXXOperatorCallExpr>(Call)) {
-    FunctionDecl *Callee = OCE->getDirectCallee();
-    if (Callee && Callee->isCXXInstanceMember() &&
-        shouldTrackImplicitObjectArg(cast<CXXMethodDecl>(Callee)))
-      VisitPointerArg(Callee, OCE->getArg(0),
-                      !Callee->getReturnType()->isReferenceType());
+
+  bool ReturnsRef = CI.Callee->getReturnType()->isReferenceType();
+
+  if (auto *CCE = dyn_cast<CXXConstructExpr>(Call)) {
+    const CXXRecordDecl *RD = CCE->getConstructor()->getParent();
+    if (CI.Args.size() > 0 && RD->hasAttr<PointerAttr>())
+      VisitPointerArg(CI.Callee->getParamDecl(0), CI.Args[0], true);
     return;
-  } else if (auto *CE = dyn_cast<CallExpr>(Call)) {
-    FunctionDecl *Callee = CE->getDirectCallee();
-    if (Callee && shouldTrackFirstArgument(Callee))
-      VisitPointerArg(Callee, CE->getArg(0),
-                      !Callee->getReturnType()->isReferenceType());
+  }
+
+  const auto LCAttr = getLifetimeAttr(CI.Callee);
+  for (unsigned I = 0; I < CI.Args.size() && I < CI.Callee->getNumParams(); ++I)
+    if (shouldTrackContract(LCAttr, CI.Callee, CI.Callee->getParamDecl(I)))
+      VisitPointerArg(CI.Callee, CI.Args[I], !ReturnsRef);
+
+  if (auto *MD = dyn_cast<CXXMethodDecl>(CI.Callee)) {
+    if (shouldTrackImplicitObjectArg(MD) ||
+        shouldTrackContract(LCAttr, CI.Callee, MD->getParent()))
+      VisitPointerArg(MD, CI.ObjectArg, !ReturnsRef);
     return;
   }
 
-  if (auto *CCE = dyn_cast<CXXConstructExpr>(Call)) {
-    const auto *Ctor = CCE->getConstructor();
-    const CXXRecordDecl *RD = Ctor->getParent();
-    if (CCE->getNumArgs() > 0 && RD->hasAttr<PointerAttr>())
-      VisitPointerArg(Ctor->getParamDecl(0), CCE->getArgs()[0], true);
+  if (auto *CE = dyn_cast<CallExpr>(Call)) {
+    if (shouldTrackFirstArgument(CI.Callee))
+      VisitPointerArg(CI.Callee, CI.Args[0], !ReturnsRef);
+    return;
   }
 }
 
@@ -6856,28 +6911,10 @@
 
 static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call,
                                         LocalVisitor Visit) {
-  const 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)
+  CallInfo CI = getCallInfo(Call);
+  if (!CI.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 = [&](const Decl *D, Expr *Arg) {
     Path.push_back({IndirectLocalPathEntry::LifetimeBoundCall, Arg, D});
     if (Arg->isGLValue())
@@ -6890,14 +6927,14 @@
     Path.pop_back();
   };
 
-  if (ObjectArg && implicitObjectParamIsLifetimeBound(Callee))
-    VisitLifetimeBoundArg(Callee, ObjectArg);
+  if (CI.ObjectArg && implicitObjectParamIsLifetimeBound(CI.Callee))
+    VisitLifetimeBoundArg(CI.Callee, CI.ObjectArg);
 
-  for (unsigned I = 0,
-                N = std::min<unsigned>(Callee->getNumParams(), Args.size());
+  for (unsigned I = 0, N = std::min<unsigned>(CI.Callee->getNumParams(),
+                                              CI.Args.size());
        I != N; ++I) {
-    if (Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
-      VisitLifetimeBoundArg(Callee->getParamDecl(I), Args[I]);
+    if (CI.Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
+      VisitLifetimeBoundArg(CI.Callee->getParamDecl(I), CI.Args[I]);
   }
 }
 
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -22,6 +22,7 @@
 #include "clang/AST/Mangle.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Basic/CharInfo.h"
+#include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TargetBuiltins.h"
 #include "clang/Basic/TargetInfo.h"
@@ -4507,6 +4508,31 @@
   }
 }
 
+
+static void handleLifetimeContractAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  LifetimeContractAttr *LCAttr;
+  if (auto *Existing = D->getAttr<LifetimeContractAttr>())
+    LCAttr = Existing;
+  else {
+    LCAttr = LifetimeContractAttr::Create(S.Context, AL.getArgAsExpr(0), AL);
+    D->addAttr(LCAttr);
+    LCAttr->PreContracts = new (S.Context) LifetimeContractMap{};
+    LCAttr->PostContracts = new (S.Context) LifetimeContractMap{};
+  }
+
+  using namespace process_lifetime_contracts;
+
+  SourceRange ErrorRange;
+  if (AL.getAttributeSpellingListIndex())
+    ErrorRange = fillContractFromExpr(AL.getArgAsExpr(0), *LCAttr->PostContracts);
+  else
+    ErrorRange = fillContractFromExpr(AL.getArgAsExpr(0), *LCAttr->PreContracts);
+
+  if (ErrorRange.isValid())
+    S.Diag(ErrorRange.getBegin(), diag::warn_unsupported_expression)
+        << ErrorRange;
+}
+
 bool Sema::CheckCallingConvAttr(const ParsedAttr &Attrs, CallingConv &CC,
                                 const FunctionDecl *FD) {
   if (Attrs.isInvalid())
@@ -7194,6 +7220,9 @@
   case ParsedAttr::AT_Pointer:
     handleLifetimeCategoryAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_LifetimeContract:
+    handleLifetimeContractAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_OpenCLKernel:
     handleSimpleAttribute<OpenCLKernelAttr>(S, D, AL);
     break;
@@ -7440,6 +7469,15 @@
     return;
   }
 
+  if (const auto *LCAttr = D->getAttr<LifetimeContractAttr>()) {
+    if (!getDiagnostics().isIgnored(diag::warn_dump_lifetime_contracts,
+                                    D->getBeginLoc())) {
+      if (const auto *FD = dyn_cast<FunctionDecl>(D))
+        Diag(D->getBeginLoc(), diag::warn_dump_lifetime_contracts)
+            << LCAttr->dumpContracts(FD);
+    }
+  }
+
   // FIXME: We should be able to handle this in TableGen as well. It would be
   // good to have a way to specify "these attributes must appear as a group",
   // for these. Additionally, it would be good to have a way to specify "these
Index: clang/lib/AST/LifetimeAttr.cpp
===================================================================
--- /dev/null
+++ clang/lib/AST/LifetimeAttr.cpp
@@ -0,0 +1,146 @@
+//===--- SemaType.cpp - Semantic Analysis for Types -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/LifetimeAttr.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/Basic/SourceLocation.h"
+
+namespace clang {
+namespace process_lifetime_contracts {
+// Easier access the attribute's representation.
+
+static const Expr *ignoreReturnValues(const Expr *E) {
+  const Expr *Original;
+  do {
+    Original = E;
+    E = E->IgnoreImplicit();
+    if (const auto *CE = dyn_cast<CXXConstructExpr>(E)) {
+      const auto *Ctor = CE->getConstructor();
+      if (Ctor->getParent()->getName() == "PSet")
+        return CE;
+      E = CE->getArg(0);
+    }
+    if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(E)) {
+      if (llvm::isa_and_nonnull<CXXConversionDecl>(MCE->getDirectCallee()))
+        E = MCE->getImplicitObjectArgument();
+    }
+  } while (E != Original);
+  return E;
+}
+
+// This function can either collect the PSets of the symbols based on a lookup
+// table or just the symbols into a pset if the lookup table is nullptr.
+static LifetimeContractSet collectPSet(const Expr *E,
+                                       const LifetimeContractMap *Lookup,
+                                       SourceRange *FailRange) {
+  if (const auto *TE = dyn_cast<CXXThisExpr>(E))
+    return LifetimeContractSet{
+        ContractVariable(TE->getType()->getPointeeCXXRecordDecl())};
+  if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
+    const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
+    if (!VD) {
+      *FailRange = DRE->getSourceRange();
+      return LifetimeContractSet{};
+    }
+    StringRef Name = VD->getName();
+    if (Name == "Null")
+      return LifetimeContractSet{ContractVariable::nullVal()};
+    else if (Name == "Static")
+      return LifetimeContractSet{ContractVariable::staticVal()};
+    else if (Name == "Invalid")
+      return LifetimeContractSet{ContractVariable::invalid()};
+    else if (Name == "Return") // TODO: function name, but overloads?
+      return LifetimeContractSet{ContractVariable::returnVal()};
+    else {
+      const auto *PVD = dyn_cast<ParmVarDecl>(VD);
+      if (!PVD) {
+        *FailRange = DRE->getSourceRange();
+        return LifetimeContractSet{};
+      }
+      if (Lookup) {
+        auto it = Lookup->find(ContractVariable(PVD));
+        if (it != Lookup->end())
+          return it->second;
+      }
+      return LifetimeContractSet{ContractVariable{PVD}};
+    }
+    *FailRange = DRE->getSourceRange();
+    return LifetimeContractSet{};
+  }
+  if (const auto *CE = dyn_cast<CallExpr>(E)) {
+    const FunctionDecl *FD = CE->getDirectCallee();
+    if (!FD || !FD->getIdentifier() || FD->getName() != "deref") {
+      *FailRange = CE->getSourceRange();
+      return LifetimeContractSet{};
+    }
+    LifetimeContractSet Result =
+        collectPSet(ignoreReturnValues(CE->getArg(0)), Lookup, FailRange);
+    auto VarsCopy = Result;
+    Result.clear();
+    for (auto Var : VarsCopy)
+      Result.insert(Var.deref());
+    return Result;
+  }
+  auto processArgs = [&](ArrayRef<const Expr *> Args) {
+    LifetimeContractSet Result;
+    for (const auto *Arg : Args) {
+      LifetimeContractSet Elem =
+          collectPSet(ignoreReturnValues(Arg), Lookup, FailRange);
+      if (Elem.empty())
+        return Elem;
+      Result.insert(Elem.begin(), Elem.end());
+    }
+    return Result;
+  };
+  if (const auto *CE = dyn_cast<CXXConstructExpr>(E))
+    return processArgs({CE->getArgs(), CE->getNumArgs()});
+  if (const auto *IE = dyn_cast<InitListExpr>(E))
+    return processArgs(IE->inits());
+  *FailRange = E->getSourceRange();
+  return LifetimeContractSet{};
+}
+
+SourceRange fillContractFromExpr(const Expr *E, LifetimeContractMap &Fill) {
+  const auto *CE = dyn_cast<CallExpr>(E);
+  if (!CE)
+    return E->getSourceRange();
+  do {
+    if (const auto *ULE = dyn_cast<UnresolvedLookupExpr>(CE->getCallee())) {
+      if (ULE->getName().isIdentifier() &&
+          ULE->getName().getAsIdentifierInfo()->getName() == "lifetime")
+        break;
+    }
+    const FunctionDecl *FD = CE->getDirectCallee();
+    if (!FD || !FD->getIdentifier() || FD->getName() != "lifetime")
+      return E->getSourceRange();
+  } while (false);
+
+  const Expr *LHS = ignoreReturnValues(CE->getArg(0));
+  if (!LHS)
+    return CE->getArg(0)->getSourceRange();
+  const Expr *RHS = ignoreReturnValues(CE->getArg(1));
+  if (!RHS)
+    return CE->getArg(1)->getSourceRange();
+
+  SourceRange ErrorRange;
+  LifetimeContractSet LhsPSet = collectPSet(LHS, nullptr, &ErrorRange);
+  if (LhsPSet.size() != 1)
+    return LHS->getSourceRange();
+  if (ErrorRange.isValid())
+    return ErrorRange;
+
+  ContractVariable VD = *LhsPSet.begin();
+  LifetimeContractSet RhsPSet = collectPSet(RHS, &Fill, &ErrorRange);
+  if (ErrorRange.isValid())
+    return ErrorRange;
+  Fill[VD] = RhsPSet;
+  return SourceRange();
+}
+} // namespace process_lifetime_contracts
+} // namespace clang
Index: clang/lib/AST/CMakeLists.txt
===================================================================
--- clang/lib/AST/CMakeLists.txt
+++ clang/lib/AST/CMakeLists.txt
@@ -77,6 +77,7 @@
   ItaniumCXXABI.cpp
   ItaniumMangle.cpp
   JSONNodeDumper.cpp
+  LifetimeAttr.cpp
   Mangle.cpp
   MicrosoftCXXABI.cpp
   MicrosoftMangle.cpp
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3336,6 +3336,11 @@
   "argument not in expected state; expected '%0', observed '%1'">,
   InGroup<Consumed>, DefaultIgnore;
 
+// Lifetime Analysis
+def warn_unsupported_expression : Warning<"this pre/postcondition is not supported">,
+  InGroup<LifetimeAnalysis>, DefaultIgnore;
+def warn_dump_lifetime_contracts : Warning<"%0">, InGroup<LifetimeDumpContracts>, DefaultIgnore;
+
 // no_sanitize attribute
 def warn_unknown_sanitizer_ignored : Warning<
   "unknown sanitizer '%0' ignored">, InGroup<UnknownSanitizers>;
Index: clang/include/clang/Basic/DiagnosticGroups.td
===================================================================
--- clang/include/clang/Basic/DiagnosticGroups.td
+++ clang/include/clang/Basic/DiagnosticGroups.td
@@ -906,6 +906,9 @@
 // Uniqueness Analysis warnings
 def Consumed       : DiagGroup<"consumed">;
 
+def LifetimeAnalysis : DiagGroup<"lifetime">;
+def LifetimeDumpContracts : DiagGroup<"lifetime-dump-contracts">;
+
 // Note that putting warnings in -Wall will not disable them by default. If a
 // warning should be active _only_ when -Wall is passed in, mark it as
 // DefaultIgnore in addition to putting it here.
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -2889,6 +2889,48 @@
   let Documentation = [LifetimePointerDocs];
 }
 
+def LifetimeContract : InheritableAttr {
+  let Spellings = [CXX11<"gsl", "pre">, CXX11<"gsl", "post">];
+  let Accessors = [Accessor<"isPre", [CXX11<"gsl", "pre">]>,
+                   Accessor<"isPost", [CXX11<"gsl", "post">]>];
+  let Subjects = SubjectList<[Function]>; // TODO: HasFunctionProto?
+  let Args = [ExprArgument<"Expr">];
+  let LateParsed = 1;
+  let TemplateDependent = 1;
+  let ParseArgumentsAsUnevaluated = 1;
+  let AdditionalMembers = [{
+public:
+  LifetimeContractMap *PreContracts;
+  LifetimeContractMap *PostContracts;
+
+  static std::string dumpSet(const LifetimeContractSet &PSet,
+                             const FunctionDecl *FD) {
+    std::string Buffer;
+    llvm::raw_string_ostream OS(Buffer);
+    OS << "{ ";
+    for (const auto &CV : PSet)
+      OS << CV.dump(FD) << " ";
+    OS << "}";
+    return OS.str();
+  }
+
+  std::string dumpContracts(const FunctionDecl *FD) const {
+    std::string Buffer;
+    llvm::raw_string_ostream OS(Buffer);
+    OS << "Pre {";
+    for (const auto &P : *PreContracts)
+      OS << " " << P.first.dump(FD) << " -> " << dumpSet(P.second, FD) << ";";
+    OS << " }";
+    OS << "  Post {";
+    for (const auto &P : *PostContracts)
+      OS << " " << P.first.dump(FD) << " -> " << dumpSet(P.second, FD) << ";";
+    OS << " }";
+    return OS.str();
+  }
+  }];
+  let Documentation = [Undocumented]; // FIXME
+}
+
 // Microsoft-related attributes
 
 def MSNoVTable : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> {
Index: clang/include/clang/AST/LifetimeAttr.h
===================================================================
--- /dev/null
+++ clang/include/clang/AST/LifetimeAttr.h
@@ -0,0 +1,159 @@
+//===--- LifetimeAttrData.h - Classes for lifetime attributes ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines classes that are used by lifetime attributes.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_LIFETIMEATTR_H
+#define LLVM_CLANG_AST_LIFETIMEATTR_H
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include <set>
+
+namespace clang {
+
+/// This represents an abstract memory location that is used in the lifetime
+/// contract representation.
+class ContractVariable {
+public:
+  ContractVariable(const ParmVarDecl *PVD, int Deref = 0)
+      : ParamIdx(PVD->getFunctionScopeIndex()), Tag(Param) {
+    deref(Deref);
+  }
+
+  ContractVariable(const RecordDecl *RD) : RD(RD), Tag(This) {}
+
+  static ContractVariable returnVal() { return ContractVariable(Return); }
+  static ContractVariable staticVal() { return ContractVariable(Static); }
+  static ContractVariable nullVal() { return ContractVariable(Null); }
+  static ContractVariable invalid() { return ContractVariable(Invalid); }
+
+  bool operator==(const ContractVariable &O) const {
+    if (Tag != O.Tag)
+      return false;
+    if (FDs != O.FDs)
+      return false;
+    if (Tag == Param)
+      return ParamIdx == O.ParamIdx;
+    if (Tag == This)
+      return RD == O.RD;
+    return true;
+  }
+
+  bool operator!=(const ContractVariable &O) const { return !(*this == O); }
+
+  bool operator<(const ContractVariable &O) const {
+    if (Tag != O.Tag)
+      return Tag < O.Tag;
+    if (FDs.size() != O.FDs.size())
+      return FDs.size() < O.FDs.size();
+    if (Tag == Param)
+      if (ParamIdx != O.ParamIdx)
+        return ParamIdx < O.ParamIdx;
+    if (Tag == This)
+      if (RD != O.RD)
+        return std::less<const RecordDecl *>()(RD, O.RD);
+
+    for (auto I = FDs.begin(), J = O.FDs.begin(); I != FDs.end(); ++I, ++J) {
+      if (*I != *J)
+        return std::less<const FieldDecl *>()(*I, *J);
+    }
+    return false;
+  }
+
+  bool isThisPointer() const { return Tag == This; }
+
+  const ParmVarDecl *asParmVarDecl(const FunctionDecl *FD) const {
+    return Tag == Param ? FD->getParamDecl(ParamIdx) : nullptr;
+  }
+
+  bool isReturnVal() const { return Tag == Return; }
+
+  // Chain of field accesses starting from VD. Types must match.
+  void addFieldRef(const FieldDecl *FD) { FDs.push_back(FD); }
+
+  ContractVariable &deref(int Num = 1) {
+    while (Num--)
+      FDs.push_back(nullptr);
+    return *this;
+  }
+
+  std::string dump(const FunctionDecl *FD) const {
+    std::string Result;
+    switch (Tag) {
+    case Null:
+      return "Null";
+    case Static:
+      return "Static";
+    case Invalid:
+      return "Invalid";
+    case This:
+      Result = "this";
+      break;
+    case Return:
+      Result = "(return value)";
+      break;
+    case Param:
+      Result = FD->getParamDecl(ParamIdx)->getName();
+      break;
+    }
+
+    for (unsigned I = 0; I < FDs.size(); ++I) {
+      if (FDs[I]) {
+        if (I > 0 && !FDs[I - 1])
+          Result = "(" + Result + ")";
+        Result += "." + std::string(FDs[I]->getName());
+      } else
+        Result.insert(0, 1, '*');
+    }
+    return Result;
+  }
+
+private:
+  union {
+    const RecordDecl *RD;
+    unsigned ParamIdx;
+  };
+
+  enum TagType {
+    Static,
+    Null,
+    Invalid,
+    This,
+    Return,
+    Param,
+  } Tag;
+
+  ContractVariable(TagType T) : Tag(T) {}
+
+  /// Possibly empty list of fields and deref operations on the base.
+  /// The First entry is the field on base, next entry is the field inside
+  /// there, etc. Null pointers represent a deref operation.
+  llvm::SmallVector<const FieldDecl *, 3> FDs;
+};
+
+using LifetimeContractSet = std::set<ContractVariable>;
+using LifetimeContractMap = std::map<ContractVariable, LifetimeContractSet>;
+
+namespace process_lifetime_contracts {
+// This function and the callees are have the sole purpose of matching the
+// AST that describes the contracts. We are only interested in identifier names
+// of function calls and variables. The AST, however, has a lot of other
+// information such as casts, termporary objects and so on. They do not have
+// any semantic meaning for contracts so much of the code is just skipping
+// these unwanted nodes. The rest is collecting the identifiers and their
+// hierarchy.
+// Also, the code might be rewritten a more simple way in the future
+// piggybacking this work: https://reviews.llvm.org/rL365355
+SourceRange fillContractFromExpr(const Expr *E, LifetimeContractMap &Fill);
+} // namespace process_lifetime_contracts
+} // namespace clang
+
+#endif // LLVM_CLANG_AST_LIFETIMEATTR_H
Index: clang/include/clang/AST/Attr.h
===================================================================
--- clang/include/clang/AST/Attr.h
+++ clang/include/clang/AST/Attr.h
@@ -17,6 +17,7 @@
 #include "clang/AST/AttrIterator.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/LifetimeAttr.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/AttrKinds.h"
 #include "clang/Basic/AttributeCommonInfo.h"
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to