[clang] [clang] Support 'this' position for lifetimebound attribute (PR #115021)

Gábor Horváth via cfe-commits cfe-commits at lists.llvm.org
Tue Nov 5 08:40:09 PST 2024


https://github.com/Xazax-hun created https://github.com/llvm/llvm-project/pull/115021

This patch makes the position -1 interpreted as the position for 'this'. Adds some basic infrastructure and support for lifetimebound attribute.

>From 77fa0b631e8743385768f3eb9ee607e9d01e9736 Mon Sep 17 00:00:00 2001
From: Gabor Horvath <gaborh at apple.com>
Date: Tue, 5 Nov 2024 16:37:57 +0000
Subject: [PATCH] [clang] Support 'this' position for lifetimebound attribute

This patch makes the position -1 interpreted as the position for 'this'.
Adds some basic infrastructure and support for lifetimebound attribute.
---
 clang/include/clang/APINotes/Types.h          | 25 +++++++++++-----
 clang/lib/APINotes/APINotesReader.cpp         | 18 ++++++++++++
 clang/lib/APINotes/APINotesTypes.cpp          |  8 +++++
 clang/lib/APINotes/APINotesWriter.cpp         | 29 +++++++++++++++----
 clang/lib/APINotes/APINotesYAMLCompiler.cpp   | 29 ++++++++++++++-----
 clang/lib/Sema/SemaAPINotes.cpp               | 16 ++++++++++
 .../Inputs/Headers/Lifetimebound.apinotes     |  4 +++
 .../APINotes/Inputs/Headers/Lifetimebound.h   |  2 +-
 clang/test/APINotes/lifetimebound.cpp         |  3 ++
 9 files changed, 114 insertions(+), 20 deletions(-)

diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
index 6327b7d75486fc..6ad1c850701146 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -445,9 +445,7 @@ class ParamInfo : public VariableInfo {
         RawRetainCountConvention() {}
 
   std::optional<bool> isNoEscape() const {
-    if (!NoEscapeSpecified)
-      return std::nullopt;
-    return NoEscape;
+    return NoEscapeSpecified ? std::optional<bool>(NoEscape) : std::nullopt;
   }
   void setNoEscape(std::optional<bool> Value) {
     NoEscapeSpecified = Value.has_value();
@@ -455,9 +453,8 @@ class ParamInfo : public VariableInfo {
   }
 
   std::optional<bool> isLifetimebound() const {
-    if (!LifetimeboundSpecified)
-      return std::nullopt;
-    return Lifetimebound;
+    return LifetimeboundSpecified ? std::optional<bool>(Lifetimebound)
+                                  : std::nullopt;
   }
   void setLifetimebound(std::optional<bool> Value) {
     LifetimeboundSpecified = Value.has_value();
@@ -643,6 +640,8 @@ class ObjCMethodInfo : public FunctionInfo {
   LLVM_PREFERRED_TYPE(bool)
   unsigned RequiredInit : 1;
 
+  std::optional<ParamInfo> Self;
+
   ObjCMethodInfo() : DesignatedInit(false), RequiredInit(false) {}
 
   friend bool operator==(const ObjCMethodInfo &, const ObjCMethodInfo &);
@@ -664,7 +663,7 @@ class ObjCMethodInfo : public FunctionInfo {
 inline bool operator==(const ObjCMethodInfo &LHS, const ObjCMethodInfo &RHS) {
   return static_cast<const FunctionInfo &>(LHS) == RHS &&
          LHS.DesignatedInit == RHS.DesignatedInit &&
-         LHS.RequiredInit == RHS.RequiredInit;
+         LHS.RequiredInit == RHS.RequiredInit && LHS.Self == RHS.Self;
 }
 
 inline bool operator!=(const ObjCMethodInfo &LHS, const ObjCMethodInfo &RHS) {
@@ -693,8 +692,20 @@ class FieldInfo : public VariableInfo {
 class CXXMethodInfo : public FunctionInfo {
 public:
   CXXMethodInfo() {}
+
+  std::optional<ParamInfo> This;
+
+  LLVM_DUMP_METHOD void dump(llvm::raw_ostream &OS);
 };
 
+inline bool operator==(const CXXMethodInfo &LHS, const CXXMethodInfo &RHS) {
+  return static_cast<const FunctionInfo &>(LHS) == RHS && LHS.This == RHS.This;
+}
+
+inline bool operator!=(const CXXMethodInfo &LHS, const CXXMethodInfo &RHS) {
+  return !(LHS == RHS);
+}
+
 /// Describes API notes data for an enumerator.
 class EnumConstantInfo : public CommonEntityInfo {
 public:
diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp
index 1bde8482fce033..1a217635a4c708 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -14,6 +14,7 @@
 //===----------------------------------------------------------------------===//
 #include "clang/APINotes/APINotesReader.h"
 #include "APINotesFormat.h"
+#include "clang/APINotes/Types.h"
 #include "llvm/ADT/Hashing.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Bitstream/BitstreamReader.h"
@@ -396,12 +397,19 @@ class ObjCMethodTableInfo
                                         const uint8_t *&Data) {
     ObjCMethodInfo Info;
     uint8_t Payload = *Data++;
+    bool HasSelf = Payload & 0x01;
+    Payload >>= 1;
     Info.RequiredInit = Payload & 0x01;
     Payload >>= 1;
     Info.DesignatedInit = Payload & 0x01;
     Payload >>= 1;
+    assert(Payload == 0 && "Wrong format.");
 
     ReadFunctionInfo(Data, Info);
+    if (HasSelf) {
+      Info.Self = ParamInfo{};
+      ReadParamInfo(Data, *Info.Self);
+    }
     return Info;
   }
 };
@@ -516,7 +524,17 @@ class CXXMethodTableInfo
   static CXXMethodInfo readUnversioned(internal_key_type Key,
                                        const uint8_t *&Data) {
     CXXMethodInfo Info;
+
+    uint8_t Payload = *Data++;
+    bool HasThis = Payload & 0x01;
+    Payload >>= 1;
+    assert(Payload == 0 && "Wrong format.");
+
     ReadFunctionInfo(Data, Info);
+    if (HasThis) {
+      Info.This = ParamInfo{};
+      ReadParamInfo(Data, *Info.This);
+    }
     return Info;
   }
 };
diff --git a/clang/lib/APINotes/APINotesTypes.cpp b/clang/lib/APINotes/APINotesTypes.cpp
index be7dec3295b4ce..b92faa0acc7482 100644
--- a/clang/lib/APINotes/APINotesTypes.cpp
+++ b/clang/lib/APINotes/APINotesTypes.cpp
@@ -85,10 +85,18 @@ LLVM_DUMP_METHOD void FunctionInfo::dump(llvm::raw_ostream &OS) const {
 
 LLVM_DUMP_METHOD void ObjCMethodInfo::dump(llvm::raw_ostream &OS) {
   static_cast<FunctionInfo &>(*this).dump(OS);
+  if (Self)
+    Self->dump(OS);
   OS << (DesignatedInit ? "[DesignatedInit] " : "")
      << (RequiredInit ? "[RequiredInit] " : "") << '\n';
 }
 
+LLVM_DUMP_METHOD void CXXMethodInfo::dump(llvm::raw_ostream &OS) {
+  static_cast<FunctionInfo &>(*this).dump(OS);
+  if (This)
+    This->dump(OS);
+}
+
 LLVM_DUMP_METHOD void TagInfo::dump(llvm::raw_ostream &OS) {
   static_cast<CommonTypeInfo &>(*this).dump(OS);
   if (HasFlagEnum)
diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp
index d81394edfde304..4e78efdfd2ae78 100644
--- a/clang/lib/APINotes/APINotesWriter.cpp
+++ b/clang/lib/APINotes/APINotesWriter.cpp
@@ -649,6 +649,7 @@ namespace {
 unsigned getVariableInfoSize(const VariableInfo &VI) {
   return 2 + getCommonEntityInfoSize(VI) + 2 + VI.getType().size();
 }
+unsigned getParamInfoSize(const ParamInfo &PI);
 
 /// Emit a serialized representation of the variable information.
 void emitVariableInfo(raw_ostream &OS, const VariableInfo &VI) {
@@ -737,6 +738,7 @@ void APINotesWriter::Implementation::writeObjCPropertyBlock(
 namespace {
 unsigned getFunctionInfoSize(const FunctionInfo &);
 void emitFunctionInfo(llvm::raw_ostream &, const FunctionInfo &);
+void emitParamInfo(raw_ostream &OS, const ParamInfo &PI);
 
 /// Used to serialize the on-disk Objective-C method table.
 class ObjCMethodTableInfo
@@ -760,7 +762,10 @@ class ObjCMethodTableInfo
   }
 
   unsigned getUnversionedInfoSize(const ObjCMethodInfo &OMI) {
-    return getFunctionInfoSize(OMI) + 1;
+    auto size = getFunctionInfoSize(OMI) + 1;
+    if (OMI.Self)
+      size += getParamInfoSize(*OMI.Self);
+    return size;
   }
 
   void emitUnversionedInfo(raw_ostream &OS, const ObjCMethodInfo &OMI) {
@@ -768,9 +773,13 @@ class ObjCMethodTableInfo
     llvm::support::endian::Writer writer(OS, llvm::endianness::little);
     flags = (flags << 1) | OMI.DesignatedInit;
     flags = (flags << 1) | OMI.RequiredInit;
+    flags = (flags << 1) | static_cast<bool>(OMI.Self);
     writer.write<uint8_t>(flags);
 
     emitFunctionInfo(OS, OMI);
+
+    if (OMI.Self)
+      emitParamInfo(OS, *OMI.Self);
   }
 };
 
@@ -793,12 +802,22 @@ class CXXMethodTableInfo
     return static_cast<size_t>(key.hashValue());
   }
 
-  unsigned getUnversionedInfoSize(const CXXMethodInfo &OMI) {
-    return getFunctionInfoSize(OMI);
+  unsigned getUnversionedInfoSize(const CXXMethodInfo &CxxMI) {
+    auto size = getFunctionInfoSize(CxxMI) + 1;
+    if (CxxMI.This)
+      size += getParamInfoSize(*CxxMI.This);
+    return size;
   }
 
-  void emitUnversionedInfo(raw_ostream &OS, const CXXMethodInfo &OMI) {
-    emitFunctionInfo(OS, OMI);
+  void emitUnversionedInfo(raw_ostream &OS, const CXXMethodInfo &CxxMI) {
+    uint8_t flags = 0;
+    llvm::support::endian::Writer writer(OS, llvm::endianness::little);
+    flags = (flags << 1) | static_cast<bool>(CxxMI.This);
+    writer.write<uint8_t>(flags);
+
+    emitFunctionInfo(OS, CxxMI);
+    if (CxxMI.This)
+      emitParamInfo(OS, *CxxMI.This);
   }
 };
 } // namespace
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index 11578e4d054b7f..634e05cfa1ab04 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -23,6 +23,7 @@
 #include "llvm/Support/VersionTuple.h"
 #include "llvm/Support/YAMLTraits.h"
 #include <optional>
+#include <type_traits>
 #include <vector>
 
 using namespace clang;
@@ -68,7 +69,7 @@ template <> struct ScalarEnumerationTraits<MethodKind> {
 
 namespace {
 struct Param {
-  unsigned Position;
+  int Position;
   std::optional<bool> NoEscape = false;
   std::optional<bool> Lifetimebound = false;
   std::optional<NullabilityKind> Nullability;
@@ -730,7 +731,9 @@ class YAMLConverter {
     }
   }
 
-  void convertParams(const ParamsSeq &Params, FunctionInfo &OutInfo) {
+  std::optional<ParamInfo> convertParams(const ParamsSeq &Params,
+                                         FunctionInfo &OutInfo) {
+    std::optional<ParamInfo> thisOrSelf;
     for (const auto &P : Params) {
       ParamInfo PI;
       if (P.Nullability)
@@ -739,10 +742,16 @@ class YAMLConverter {
       PI.setLifetimebound(P.Lifetimebound);
       PI.setType(std::string(P.Type));
       PI.setRetainCountConvention(P.RetainCountConvention);
-      if (OutInfo.Params.size() <= P.Position)
+      if (static_cast<int>(OutInfo.Params.size()) <= P.Position)
         OutInfo.Params.resize(P.Position + 1);
-      OutInfo.Params[P.Position] |= PI;
+      if (P.Position < -1)
+        emitError("parameter position smaller than -1 is not valid");
+      else if (P.Position == -1)
+        thisOrSelf = PI;
+      else
+        OutInfo.Params[P.Position] |= PI;
     }
+    return thisOrSelf;
   }
 
   void convertNullability(const NullabilitySeq &Nullability,
@@ -818,7 +827,7 @@ class YAMLConverter {
     MI.ResultType = std::string(M.ResultType);
 
     // Translate parameter information.
-    convertParams(M.Params, MI);
+    MI.Self = convertParams(M.Params, MI);
 
     // Translate nullability info.
     convertNullability(M.Nullability, M.NullabilityOfRet, MI, M.Selector);
@@ -926,11 +935,17 @@ class YAMLConverter {
                          TheNamespace.Items, SwiftVersion);
   }
 
-  void convertFunction(const Function &Function, FunctionInfo &FI) {
+  template <typename FuncOrMethodInfo>
+  void convertFunction(const Function &Function, FuncOrMethodInfo &FI) {
     convertAvailability(Function.Availability, FI, Function.Name);
     FI.setSwiftPrivate(Function.SwiftPrivate);
     FI.SwiftName = std::string(Function.SwiftName);
-    convertParams(Function.Params, FI);
+    if constexpr (std::is_same_v<FuncOrMethodInfo, CXXMethodInfo>) {
+      FI.This = convertParams(Function.Params, FI);
+    } else {
+      if (convertParams(Function.Params, FI))
+        emitError("position -1 is only valid for C++ and Objective-C methods");
+    }
     convertNullability(Function.Nullability, Function.NullabilityOfRet, FI,
                        Function.Name);
     FI.ResultType = std::string(Function.ResultType);
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index edb4c19d59745c..5bb58d6c98be95 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -10,10 +10,12 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "TypeLocBuilder.h"
 #include "clang/APINotes/APINotesReader.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
+#include "clang/AST/TypeLoc.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Lex/Lexer.h"
 #include "clang/Sema/SemaInternal.h"
@@ -567,6 +569,20 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc,
 static void ProcessAPINotes(Sema &S, CXXMethodDecl *Method,
                             const api_notes::CXXMethodInfo &Info,
                             VersionedInfoMetadata Metadata) {
+  if (Info.This && Info.This->isLifetimebound()) {
+    auto MethodType = Method->getType();
+    auto *attr = ::new (S.Context)
+        LifetimeBoundAttr(S.Context, getPlaceholderAttrInfo());
+    QualType AttributedType =
+        S.Context.getAttributedType(attr, MethodType, MethodType);
+    TypeLocBuilder TLB;
+    TLB.pushFullCopy(Method->getTypeSourceInfo()->getTypeLoc());
+    AttributedTypeLoc TyLoc = TLB.push<AttributedTypeLoc>(AttributedType);
+    TyLoc.setAttr(attr);
+    Method->setType(AttributedType);
+    Method->setTypeSourceInfo(TLB.getTypeSourceInfo(S.Context, AttributedType));
+  }
+
   ProcessAPINotes(S, (FunctionOrMethod)Method, Info, Metadata);
 }
 
diff --git a/clang/test/APINotes/Inputs/Headers/Lifetimebound.apinotes b/clang/test/APINotes/Inputs/Headers/Lifetimebound.apinotes
index d07d87cf02f025..4bd5fbb42bf04c 100644
--- a/clang/test/APINotes/Inputs/Headers/Lifetimebound.apinotes
+++ b/clang/test/APINotes/Inputs/Headers/Lifetimebound.apinotes
@@ -8,6 +8,10 @@ Functions:
 Tags:
 - Name: MyClass
   Methods:
+    - Name: annotateThis
+      Parameters:
+        - Position:      -1
+          Lifetimebound: true
     - Name: methodToAnnotate
       Parameters:
         - Position:      0
diff --git a/clang/test/APINotes/Inputs/Headers/Lifetimebound.h b/clang/test/APINotes/Inputs/Headers/Lifetimebound.h
index 2ec302f7801a77..bf034d655fb403 100644
--- a/clang/test/APINotes/Inputs/Headers/Lifetimebound.h
+++ b/clang/test/APINotes/Inputs/Headers/Lifetimebound.h
@@ -1,6 +1,6 @@
 int *funcToAnnotate(int *p);
 
-// TODO: support annotating ctors and 'this'.
+// TODO: support annotating ctors.
 struct MyClass {
     MyClass(int*);
     int *annotateThis();
diff --git a/clang/test/APINotes/lifetimebound.cpp b/clang/test/APINotes/lifetimebound.cpp
index 3e5ce9df895b70..0e040a6058e439 100644
--- a/clang/test/APINotes/lifetimebound.cpp
+++ b/clang/test/APINotes/lifetimebound.cpp
@@ -1,6 +1,7 @@
 // RUN: rm -rf %t && mkdir -p %t
 // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Lifetimebound -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers %s -x c++
 // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Lifetimebound -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter funcToAnnotate -x c++ | FileCheck --check-prefix=CHECK-PARAM %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Lifetimebound -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter annotateThis -x c++ | FileCheck --check-prefix=CHECK-METHOD-THIS %s
 // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Lifetimebound -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter methodToAnnotate -x c++ | FileCheck --check-prefix=CHECK-METHOD %s
 #include "Lifetimebound.h"
 
@@ -11,3 +12,5 @@
 // CHECK-METHOD: CXXMethodDecl {{.+}} methodToAnnotate 
 // CHECK-METHOD-NEXT: ParmVarDecl {{.+}} p
 // CHECK-METHOD-NEXT: LifetimeBoundAttr
+
+// CHECK-METHOD-THIS: CXXMethodDecl {{.+}} annotateThis 'int *() {{.*}}clang::lifetimebound



More information about the cfe-commits mailing list