[clang] [lldb] [Clang] Improve Sema diagnostic performance for __builtin_counted_by_ref (PR #116719)

Bill Wendling via cfe-commits cfe-commits at lists.llvm.org
Wed Nov 27 09:42:15 PST 2024


https://github.com/bwendling updated https://github.com/llvm/llvm-project/pull/116719

>From 2dcf18163de2ccce959f46bf82df1fa40e3fd1fc Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Fri, 15 Nov 2024 15:41:48 -0800
Subject: [PATCH 1/9] [Clang] Improve Sema diagnostic performance for
 __builtin_counted_by_ref

Implement the sema checks with a placeholder. We then check for that
placeholder in all of the places we care to emit a diagnostic.
---
 clang/include/clang/AST/ASTContext.h          |   1 +
 clang/include/clang/AST/BuiltinTypes.def      |   3 +
 .../clang/Basic/DiagnosticSemaKinds.td        |   2 +-
 .../include/clang/Serialization/ASTBitCodes.h |   5 +-
 clang/lib/AST/ASTContext.cpp                  |   4 +
 clang/lib/AST/NSAPI.cpp                       |   1 +
 clang/lib/AST/Type.cpp                        |   3 +
 clang/lib/AST/TypeLoc.cpp                     |   1 +
 clang/lib/Sema/SemaChecking.cpp               |   1 +
 clang/lib/Sema/SemaDecl.cpp                   |  11 ++
 clang/lib/Sema/SemaExpr.cpp                   | 138 +++++++++---------
 clang/lib/Sema/SemaStmt.cpp                   |  10 ++
 clang/lib/Serialization/ASTCommon.cpp         |   3 +
 clang/lib/Serialization/ASTReader.cpp         |   3 +
 clang/test/Modules/no-external-type-id.cppm   |   2 +-
 clang/test/Sema/builtin-counted-by-ref.c      |  77 +++++-----
 .../TypeSystem/Clang/TypeSystemClang.cpp      |   2 +
 17 files changed, 154 insertions(+), 113 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 89fcb6789d880a..39cad95d911a33 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1184,6 +1184,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   CanQualType DependentTy, OverloadTy, BoundMemberTy, UnresolvedTemplateTy,
       UnknownAnyTy;
   CanQualType BuiltinFnTy;
+  CanQualType BuiltinCountedByRefTy;
   CanQualType PseudoObjectTy, ARCUnbridgedCastTy;
   CanQualType ObjCBuiltinIdTy, ObjCBuiltinClassTy, ObjCBuiltinSelTy;
   CanQualType ObjCBuiltinBoolTy;
diff --git a/clang/include/clang/AST/BuiltinTypes.def b/clang/include/clang/AST/BuiltinTypes.def
index 444be4311a7431..4eae6962a46de6 100644
--- a/clang/include/clang/AST/BuiltinTypes.def
+++ b/clang/include/clang/AST/BuiltinTypes.def
@@ -314,6 +314,9 @@ PLACEHOLDER_TYPE(UnknownAny, UnknownAnyTy)
 
 PLACEHOLDER_TYPE(BuiltinFn, BuiltinFnTy)
 
+// A placeholder type for __builtin_counted_by_ref.
+PLACEHOLDER_TYPE(BuiltinCountedByRef, BuiltinCountedByRefTy)
+
 // The type of a cast which, in ARC, would normally require a
 // __bridge, but which might be okay depending on the immediate
 // context.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3caf471d3037f9..37fb44d4bf74cd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6673,7 +6673,7 @@ def err_builtin_counted_by_ref_must_be_flex_array_member : Error<
 def err_builtin_counted_by_ref_cannot_leak_reference : Error<
   "value returned by '__builtin_counted_by_ref' cannot be assigned to a "
   "variable, have its address taken, or passed into or returned from a function">;
-def err_builtin_counted_by_ref_invalid_lhs_use : Error<
+def err_builtin_counted_by_ref_invalid_use : Error<
   "value returned by '__builtin_counted_by_ref' cannot be used in "
   "%select{an array subscript|a binary}0 expression">;
 def err_builtin_counted_by_ref_has_side_effects : Error<
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index fd834c14ce790f..f4b71861968e77 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1111,6 +1111,9 @@ enum PredefinedTypeIDs {
   /// \brief The '__ibm128' type
   PREDEF_TYPE_IBM128_ID = 74,
 
+  /// \brief The placeholder type for __builtin_counted_by_ref.
+  PREDEF_TYPE_BUILTIN_COUNTED_BY_REF = 75,
+
 /// OpenCL image types with auto numeration
 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix)                   \
   PREDEF_TYPE_##Id##_ID,
@@ -1148,7 +1151,7 @@ enum PredefinedTypeIDs {
 ///
 /// Type IDs for non-predefined types will start at
 /// NUM_PREDEF_TYPE_IDs.
-const unsigned NUM_PREDEF_TYPE_IDS = 513;
+const unsigned NUM_PREDEF_TYPE_IDS = 514;
 
 // Ensure we do not overrun the predefined types we reserved
 // in the enum PredefinedTypeIDs above.
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 14fbadbc35ae5d..06226afaf23aab 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1390,6 +1390,10 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target,
   if (LangOpts.MatrixTypes)
     InitBuiltinType(IncompleteMatrixIdxTy, BuiltinType::IncompleteMatrixIdx);
 
+  // Placeholder for __builtin_counted_by_ref().
+  if (!LangOpts.CPlusPlus)
+    InitBuiltinType(BuiltinCountedByRefTy, BuiltinType::BuiltinCountedByRef);
+
   // Builtin types for 'id', 'Class', and 'SEL'.
   InitBuiltinType(ObjCBuiltinIdTy, BuiltinType::ObjCId);
   InitBuiltinType(ObjCBuiltinClassTy, BuiltinType::ObjCClass);
diff --git a/clang/lib/AST/NSAPI.cpp b/clang/lib/AST/NSAPI.cpp
index 311fec32bbfa90..6a722b89af6205 100644
--- a/clang/lib/AST/NSAPI.cpp
+++ b/clang/lib/AST/NSAPI.cpp
@@ -466,6 +466,7 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const {
   case BuiltinType::Half:
   case BuiltinType::PseudoObject:
   case BuiltinType::BuiltinFn:
+  case BuiltinType::BuiltinCountedByRef:
   case BuiltinType::IncompleteMatrixIdx:
   case BuiltinType::ArraySection:
   case BuiltinType::OMPArrayShaping:
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index b70f86ef31442d..1e44c2bd234fbb 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3444,6 +3444,8 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const {
     return "<ARC unbridged cast type>";
   case BuiltinFn:
     return "<builtin fn type>";
+  case BuiltinCountedByRef:
+    return "<builtin counted by ref type>";
   case ObjCId:
     return "id";
   case ObjCClass:
@@ -4861,6 +4863,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
 #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
 #include "clang/Basic/HLSLIntangibleTypes.def"
     case BuiltinType::BuiltinFn:
+    case BuiltinType::BuiltinCountedByRef:
     case BuiltinType::NullPtr:
     case BuiltinType::IncompleteMatrixIdx:
     case BuiltinType::ArraySection:
diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp
index fbb7fc5cd76902..088bd5c379d8c4 100644
--- a/clang/lib/AST/TypeLoc.cpp
+++ b/clang/lib/AST/TypeLoc.cpp
@@ -433,6 +433,7 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const {
 #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
 #include "clang/Basic/HLSLIntangibleTypes.def"
   case BuiltinType::BuiltinFn:
+  case BuiltinType::BuiltinCountedByRef:
   case BuiltinType::IncompleteMatrixIdx:
   case BuiltinType::ArraySection:
   case BuiltinType::OMPArrayShaping:
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2d4a7cd287b70d..52b47a995624f4 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -755,6 +755,7 @@ static ExprResult BuiltinDumpStruct(Sema &S, CallExpr *TheCall) {
     case BuiltinType::PseudoObject:
     case BuiltinType::UnknownAny:
     case BuiltinType::BuiltinFn:
+    case BuiltinType::BuiltinCountedByRef:
       // This might be a callable.
       break;
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a36ca61a1bef30..d4a90cf262b09f 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14690,6 +14690,17 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) {
     }
   }
 
+  // The result of __builtin_counted_by_ref cannot be assigned to a variable.
+  // It allows leaking and modification of bounds safety information.
+  if (const auto *CE = dyn_cast_if_present<CallExpr>(VD->getInit())) {
+    const Expr *E = CE->getCallee()->IgnoreParenImpCasts();
+    if (const BuiltinType *PTy = E->getType()->getAsPlaceholderType();
+        PTy && PTy->getKind() == BuiltinType::BuiltinCountedByRef)
+      Diag(E->getExprLoc(),
+           diag::err_builtin_counted_by_ref_cannot_leak_reference)
+          << E->getSourceRange();
+  }
+
   checkAttributesAfterMerging(*this, *VD);
 
   if (VD->isStaticLocal())
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index dcf495b700540f..f1925bae9b91e3 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -3382,7 +3382,9 @@ ExprResult Sema::BuildDeclarationNameExpr(
   case Decl::Function: {
     if (unsigned BID = cast<FunctionDecl>(VD)->getBuiltinID()) {
       if (!Context.BuiltinInfo.isDirectlyAddressable(BID)) {
-        type = Context.BuiltinFnTy;
+        type = (BID == Builtin::BI__builtin_counted_by_ref)
+                   ? Context.BuiltinCountedByRefTy
+                   : Context.BuiltinFnTy;
         valueKind = VK_PRValue;
         break;
       }
@@ -4894,6 +4896,18 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base,
     return ExprError();
   }
 
+  // We cannot use __builtin_counted_by_ref in a binary expression. It's
+  // possible to leak the reference and violate bounds security.
+  Expr *E = base->IgnoreParenImpCasts();
+  if (auto *CE = dyn_cast<CallExpr>(E)) {
+    E = CE->getCallee()->IgnoreParenImpCasts();
+    if (const BuiltinType *PTy = E->getType()->getAsPlaceholderType();
+        PTy && PTy->getKind() == BuiltinType::BuiltinCountedByRef) {
+      Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
+          << 0 << E->getSourceRange();
+    }
+  }
+
   // Handle any non-overload placeholder types in the base and index
   // expressions.  We can't handle overloads here because the other
   // operand might be an overloadable type, in which case the overload
@@ -6188,6 +6202,7 @@ static bool isPlaceholderToRemoveAsArg(QualType type) {
   // These are always invalid as call arguments and should be reported.
   case BuiltinType::BoundMember:
   case BuiltinType::BuiltinFn:
+  case BuiltinType::BuiltinCountedByRef:
   case BuiltinType::IncompleteMatrixIdx:
   case BuiltinType::ArraySection:
   case BuiltinType::OMPArrayShaping:
@@ -6205,10 +6220,27 @@ bool Sema::CheckArgsForPlaceholders(MultiExprArg args) {
   for (size_t i = 0, e = args.size(); i != e; i++) {
     if (isPlaceholderToRemoveAsArg(args[i]->getType())) {
       ExprResult result = CheckPlaceholderExpr(args[i]);
-      if (result.isInvalid()) hasInvalid = true;
-      else args[i] = result.get();
+      if (result.isInvalid())
+        hasInvalid = true;
+      else
+        args[i] = result.get();
+    }
+
+    // The result of __builtin_counted_by_ref cannot be used as a function
+    // argument. It allows leaking and modification of bounds safety
+    // information.
+    if (const auto *CE = dyn_cast<CallExpr>(args[i])) {
+      const Expr *E = CE->getCallee()->IgnoreParenImpCasts();
+      if (const BuiltinType *PTy = E->getType()->getAsPlaceholderType();
+          PTy && PTy->getKind() == BuiltinType::BuiltinCountedByRef) {
+        hasInvalid = true;
+        Diag(E->getExprLoc(),
+             diag::err_builtin_counted_by_ref_cannot_leak_reference)
+            << E->getSourceRange();
+      }
     }
   }
+
   return hasInvalid;
 }
 
@@ -6770,7 +6802,9 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
   ExprResult Result;
   QualType ResultTy;
   if (BuiltinID &&
-      Fn->getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn)) {
+      (Fn->getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn) ||
+       Fn->getType()->isSpecificBuiltinType(
+           BuiltinType::BuiltinCountedByRef))) {
     // Extract the return type from the (builtin) function pointer type.
     // FIXME Several builtins still have setType in
     // Sema::CheckBuiltinFunctionCall. One should review their definitions in
@@ -9196,38 +9230,6 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS,
   LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType();
   RHSType = Context.getCanonicalType(RHSType).getUnqualifiedType();
 
-  // __builtin_counted_by_ref cannot be assigned to a variable, used in
-  // function call, or in a return.
-  auto FindBuiltinCountedByRefExpr = [&](Expr *E) -> CallExpr * {
-    struct BuiltinCountedByRefVisitor : DynamicRecursiveASTVisitor {
-      CallExpr *TheCall = nullptr;
-      bool VisitCallExpr(CallExpr *CE) override {
-        if (CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref) {
-          TheCall = CE;
-          return false;
-        }
-        return true;
-      }
-      bool
-      VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UE) override {
-        // A UnaryExprOrTypeTraitExpr---e.g. sizeof, __alignof, etc.---isn't
-        // the same as a CallExpr, so if we find a __builtin_counted_by_ref()
-        // call in one, ignore it.
-        return false;
-      }
-    } V;
-    V.TraverseStmt(E);
-    return V.TheCall;
-  };
-  static llvm::SmallPtrSet<CallExpr *, 4> Diagnosed;
-  if (auto *CE = FindBuiltinCountedByRefExpr(RHS.get());
-      CE && !Diagnosed.count(CE)) {
-    Diagnosed.insert(CE);
-    Diag(CE->getExprLoc(),
-         diag::err_builtin_counted_by_ref_cannot_leak_reference)
-        << CE->getSourceRange();
-  }
-
   // Common case: no conversion required.
   if (LHSType == RHSType) {
     Kind = CK_NoOp;
@@ -13778,42 +13780,6 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
     ConvTy = CheckAssignmentConstraints(Loc, LHSType, RHSType);
   }
 
-  // __builtin_counted_by_ref can't be used in a binary expression or array
-  // subscript on the LHS.
-  int DiagOption = -1;
-  auto FindInvalidUseOfBoundsSafetyCounter = [&](Expr *E) -> CallExpr * {
-    struct BuiltinCountedByRefVisitor : DynamicRecursiveASTVisitor {
-      CallExpr *CE = nullptr;
-      bool InvalidUse = false;
-      int Option = -1;
-
-      bool VisitCallExpr(CallExpr *E) override {
-        if (E->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref) {
-          CE = E;
-          return false;
-        }
-        return true;
-      }
-
-      bool VisitArraySubscriptExpr(ArraySubscriptExpr *E) override {
-        InvalidUse = true;
-        Option = 0; // report 'array expression' in diagnostic.
-        return true;
-      }
-      bool VisitBinaryOperator(BinaryOperator *E) override {
-        InvalidUse = true;
-        Option = 1; // report 'binary expression' in diagnostic.
-        return true;
-      }
-    } V;
-    V.TraverseStmt(E);
-    DiagOption = V.Option;
-    return V.InvalidUse ? V.CE : nullptr;
-  };
-  if (auto *CE = FindInvalidUseOfBoundsSafetyCounter(LHSExpr))
-    Diag(CE->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_lhs_use)
-        << DiagOption << CE->getSourceRange();
-
   if (DiagnoseAssignmentResult(ConvTy, Loc, LHSType, RHSType, RHS.get(),
                                AssignmentAction::Assigning))
     return QualType();
@@ -15274,6 +15240,27 @@ ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
   if (Kind == tok::TokenKind::slash)
     DetectPrecisionLossInComplexDivision(*this, TokLoc, LHSExpr);
 
+  // We cannot use __builtin_counted_by_ref in a binary expression. It's
+  // possible to leak the reference and violate bounds security.
+  auto CheckBuiltinCountedByRefPlaceholder = [&](const Expr *E) {
+    if (const auto *CE = dyn_cast<CallExpr>(E->IgnoreParenImpCasts())) {
+      E = CE->getCallee()->IgnoreParenImpCasts();
+      if (const BuiltinType *PTy = E->getType()->getAsPlaceholderType();
+          PTy && PTy->getKind() == BuiltinType::BuiltinCountedByRef) {
+        if (BinaryOperator::isAssignmentOp(Opc))
+          Diag(E->getExprLoc(),
+               diag::err_builtin_counted_by_ref_cannot_leak_reference)
+              << E->getSourceRange();
+        else
+          Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
+              << 1 << E->getSourceRange();
+      }
+    }
+  };
+
+  CheckBuiltinCountedByRefPlaceholder(LHSExpr);
+  CheckBuiltinCountedByRefPlaceholder(RHSExpr);
+
   return BuildBinOp(S, TokLoc, Opc, LHSExpr, RHSExpr);
 }
 
@@ -21074,6 +21061,13 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) {
     return ExprError();
   }
 
+  case BuiltinType::BuiltinCountedByRef: {
+    Diag(E->IgnoreParens()->getExprLoc(),
+         diag::err_builtin_counted_by_ref_cannot_leak_reference)
+        << E->IgnoreParens()->getSourceRange();
+    return ExprError();
+  }
+
   case BuiltinType::IncompleteMatrixIdx:
     Diag(cast<MatrixSubscriptExpr>(E->IgnoreParens())
              ->getRowIdx()
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index f3ee5211acdd11..024dc05f6c5636 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -3765,6 +3765,16 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
         << FSI->getFirstCoroutineStmtKeyword();
   }
 
+  if (const auto *CE = dyn_cast_if_present<CallExpr>(RetVal.get())) {
+    const Expr *E = CE->getCallee()->IgnoreParenImpCasts();
+    if (const BuiltinType *PTy = E->getType()->getAsPlaceholderType();
+        PTy && PTy->getKind() == BuiltinType::BuiltinCountedByRef) {
+      Diag(E->getExprLoc(),
+           diag::err_builtin_counted_by_ref_cannot_leak_reference)
+          << E->getSourceRange();
+    }
+  }
+
   StmtResult R =
       BuildReturnStmt(ReturnLoc, RetVal.get(), /*AllowRecovery=*/true);
   if (R.isInvalid() || ExprEvalContexts.back().isDiscardedStatementContext())
diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp
index ec18e84255ca8e..e7f54cf8839c70 100644
--- a/clang/lib/Serialization/ASTCommon.cpp
+++ b/clang/lib/Serialization/ASTCommon.cpp
@@ -274,6 +274,9 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) {
   case BuiltinType::BuiltinFn:
     ID = PREDEF_TYPE_BUILTIN_FN;
     break;
+  case BuiltinType::BuiltinCountedByRef:
+    ID = PREDEF_TYPE_BUILTIN_COUNTED_BY_REF;
+    break;
   case BuiltinType::IncompleteMatrixIdx:
     ID = PREDEF_TYPE_INCOMPLETE_MATRIX_IDX;
     break;
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index ec85fad3389a1c..76dfaa5481e424 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7458,6 +7458,9 @@ QualType ASTReader::GetType(TypeID ID) {
     case PREDEF_TYPE_BUILTIN_FN:
       T = Context.BuiltinFnTy;
       break;
+    case PREDEF_TYPE_BUILTIN_COUNTED_BY_REF:
+      T = Context.BuiltinCountedByRefTy;
+      break;
     case PREDEF_TYPE_INCOMPLETE_MATRIX_IDX:
       T = Context.IncompleteMatrixIdxTy;
       break;
diff --git a/clang/test/Modules/no-external-type-id.cppm b/clang/test/Modules/no-external-type-id.cppm
index d067e574e72e37..162300478632db 100644
--- a/clang/test/Modules/no-external-type-id.cppm
+++ b/clang/test/Modules/no-external-type-id.cppm
@@ -23,7 +23,7 @@ export module b;
 import a;
 export int b();
 
-// CHECK: <DECL_FUNCTION {{.*}} op8=4120
+// CHECK: <DECL_FUNCTION {{.*}} op8=4128
 // CHECK: <TYPE_FUNCTION_PROTO
 
 //--- a.v1.cppm
diff --git a/clang/test/Sema/builtin-counted-by-ref.c b/clang/test/Sema/builtin-counted-by-ref.c
index 5a7ecefcb78976..c21c7093468dde 100644
--- a/clang/test/Sema/builtin-counted-by-ref.c
+++ b/clang/test/Sema/builtin-counted-by-ref.c
@@ -11,62 +11,63 @@ struct fam_struct {
   int array[] __attribute__((counted_by(count)));
 };
 
-void test1(struct fam_struct *ptr, int size, int idx) {
-  size_t size_of = sizeof(__builtin_counted_by_ref(ptr->array)); // ok
-
-  *__builtin_counted_by_ref(ptr->array) = size;             // ok
+void g(char *);
+void h(char);
 
-  {
-      size_t __ignored_assignment;
-      *_Generic(__builtin_counted_by_ref(ptr->array),
-               void *: &__ignored_assignment,
-               default: __builtin_counted_by_ref(ptr->array)) = 42; // ok
-  }
+void test1(struct fam_struct *ptr, int size, int idx) {
+  size_t size_of = sizeof(__builtin_counted_by_ref(ptr->array));    // ok
+  int align_of = __alignof(__builtin_counted_by_ref(ptr->array));   // ok
+  size_t __ignored_assignment;
+
+  *__builtin_counted_by_ref(ptr->array) = size;                     // ok
+  *_Generic(__builtin_counted_by_ref(ptr->array),
+           void *: &__ignored_assignment,
+           default: __builtin_counted_by_ref(ptr->array)) = 42;     // ok
+  h(*__builtin_counted_by_ref(ptr->array));                         // ok
 }
 
 void test2(struct fam_struct *ptr, int idx) {
-  __builtin_counted_by_ref();                               // expected-error {{too few arguments to function call, expected 1, have 0}}
-  __builtin_counted_by_ref(ptr->array, ptr->x, ptr->count); // expected-error {{too many arguments to function call, expected 1, have 3}}
+  __builtin_counted_by_ref();                                       // expected-error {{too few arguments to function call, expected 1, have 0}}
+  __builtin_counted_by_ref(ptr->array, ptr->x, ptr->count);         // expected-error {{too many arguments to function call, expected 1, have 3}}
 }
 
 void test3(struct fam_struct *ptr, int idx) {
-  __builtin_counted_by_ref(&ptr->array[0]);                 // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
-  __builtin_counted_by_ref(&ptr->array[idx]);               // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
-  __builtin_counted_by_ref(&ptr->array);                    // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
-  __builtin_counted_by_ref(ptr->x);                         // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
-  __builtin_counted_by_ref(&ptr->x);                        // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
-  __builtin_counted_by_ref(global_array);                   // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
-  __builtin_counted_by_ref(global_int);                     // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
-  __builtin_counted_by_ref(&global_int);                    // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
+  __builtin_counted_by_ref(&ptr->array[0]);                         // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
+  __builtin_counted_by_ref(&ptr->array[idx]);                       // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
+  __builtin_counted_by_ref(&ptr->array);                            // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
+  __builtin_counted_by_ref(ptr->x);                                 // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
+  __builtin_counted_by_ref(&ptr->x);                                // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
+  __builtin_counted_by_ref(global_array);                           // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
+  __builtin_counted_by_ref(global_int);                             // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
+  __builtin_counted_by_ref(&global_int);                            // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
 }
 
 void test4(struct fam_struct *ptr, int idx) {
-  __builtin_counted_by_ref(ptr++->array);                   // expected-error {{'__builtin_counted_by_ref' argument cannot have side-effects}}
-  __builtin_counted_by_ref(&ptr->array[idx++]);             // expected-error {{'__builtin_counted_by_ref' argument cannot have side-effects}}
+  __builtin_counted_by_ref(ptr++->array);                           // expected-error {{'__builtin_counted_by_ref' argument cannot have side-effects}}
+  __builtin_counted_by_ref(&ptr->array[idx++]);                     // expected-error {{'__builtin_counted_by_ref' argument cannot have side-effects}}
 }
 
-void foo(char *);
-
 void *test5(struct fam_struct *ptr, int size, int idx) {
-  char *ref = __builtin_counted_by_ref(ptr->array);         // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
+  char *ref = __builtin_counted_by_ref(ptr->array);                 // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
+  char *int_ptr;
+  char *p;
 
-  ref = __builtin_counted_by_ref(ptr->array);               // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
-  ref = (char *)(int *)(42 + &*__builtin_counted_by_ref(ptr->array)); // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
-  foo(__builtin_counted_by_ref(ptr->array));                // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
-  foo(ref = __builtin_counted_by_ref(ptr->array));          // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
+  ref = __builtin_counted_by_ref(ptr->array);                       // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
+  g(__builtin_counted_by_ref(ptr->array));                          // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
+  g(ref = __builtin_counted_by_ref(ptr->array));                    // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
 
-  if ((ref = __builtin_counted_by_ref(ptr->array)))         // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
+  if ((ref = __builtin_counted_by_ref(ptr->array)))                 // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
     ;
 
-  for (char *p = __builtin_counted_by_ref(ptr->array); p && *p; ++p) // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
+  for (p = __builtin_counted_by_ref(ptr->array); p && *p; ++p)      // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
     ;
 
-  return __builtin_counted_by_ref(ptr->array);              // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
+  return __builtin_counted_by_ref(ptr->array);                      // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
 }
 
 void test6(struct fam_struct *ptr, int size, int idx) {
-  *(__builtin_counted_by_ref(ptr->array) + 4) = 37;         // expected-error {{value returned by '__builtin_counted_by_ref' cannot be used in a binary expression}}
-  __builtin_counted_by_ref(ptr->array)[3] = 37;             // expected-error {{value returned by '__builtin_counted_by_ref' cannot be used in an array subscript expression}}
+  *(__builtin_counted_by_ref(ptr->array) + 4) = 37;                 // expected-error {{value returned by '__builtin_counted_by_ref' cannot be used in a binary expression}}
+  __builtin_counted_by_ref(ptr->array)[3] = 37;                     // expected-error {{value returned by '__builtin_counted_by_ref' cannot be used in an array subscript expression}}
 }
 
 struct non_fam_struct {
@@ -77,10 +78,10 @@ struct non_fam_struct {
 };
 
 void *test7(struct non_fam_struct *ptr, int size) {
-  *__builtin_counted_by_ref(ptr->array) = size              // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
-  *__builtin_counted_by_ref(&ptr->array[0]) = size;         // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
-  *__builtin_counted_by_ref(ptr->pointer) = size;           // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
-  *__builtin_counted_by_ref(&ptr->pointer[0]) = size;       // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
+  *__builtin_counted_by_ref(ptr->array) = size                      // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
+  *__builtin_counted_by_ref(&ptr->array[0]) = size;                 // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
+  *__builtin_counted_by_ref(ptr->pointer) = size;                   // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
+  *__builtin_counted_by_ref(&ptr->pointer[0]) = size;               // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
 }
 
 struct char_count {
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 5f8163211857c3..4e02acff3f2076 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -4951,6 +4951,7 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type,
     case clang::BuiltinType::Kind::ARCUnbridgedCast:
     case clang::BuiltinType::Kind::BoundMember:
     case clang::BuiltinType::Kind::BuiltinFn:
+    case clang::BuiltinType::Kind::BuiltinCountedByRef:
     case clang::BuiltinType::Kind::Dependent:
     case clang::BuiltinType::Kind::OCLClkEvent:
     case clang::BuiltinType::Kind::OCLEvent:
@@ -6111,6 +6112,7 @@ uint32_t TypeSystemClang::GetNumPointeeChildren(clang::QualType type) {
     case clang::BuiltinType::ARCUnbridgedCast:
     case clang::BuiltinType::PseudoObject:
     case clang::BuiltinType::BuiltinFn:
+    case clang::BuiltinType::BuiltinCountedByRef:
     case clang::BuiltinType::ArraySection:
       return 1;
     default:

>From b32983c5159634df24fbb53c47e0e5bb3e8f2f45 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Tue, 19 Nov 2024 17:32:09 -0800
Subject: [PATCH 2/9] Don't use a placeholder. Instead, place diagnostics in
 the situations we want to disallow.

---
 clang/include/clang/AST/ASTContext.h          |  1 -
 clang/include/clang/AST/BuiltinTypes.def      |  3 -
 .../include/clang/Serialization/ASTBitCodes.h |  5 +-
 clang/lib/AST/ASTContext.cpp                  |  4 --
 clang/lib/AST/NSAPI.cpp                       |  1 -
 clang/lib/AST/Type.cpp                        |  3 -
 clang/lib/AST/TypeLoc.cpp                     |  1 -
 clang/lib/Sema/SemaChecking.cpp               |  1 -
 clang/lib/Sema/SemaDecl.cpp                   | 13 ++--
 clang/lib/Sema/SemaExpr.cpp                   | 71 +++++++------------
 clang/lib/Sema/SemaStmt.cpp                   | 14 ++--
 clang/lib/Serialization/ASTCommon.cpp         |  3 -
 clang/lib/Serialization/ASTReader.cpp         |  3 -
 clang/test/Modules/no-external-type-id.cppm   |  2 +-
 14 files changed, 36 insertions(+), 89 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 39cad95d911a33..89fcb6789d880a 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1184,7 +1184,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
   CanQualType DependentTy, OverloadTy, BoundMemberTy, UnresolvedTemplateTy,
       UnknownAnyTy;
   CanQualType BuiltinFnTy;
-  CanQualType BuiltinCountedByRefTy;
   CanQualType PseudoObjectTy, ARCUnbridgedCastTy;
   CanQualType ObjCBuiltinIdTy, ObjCBuiltinClassTy, ObjCBuiltinSelTy;
   CanQualType ObjCBuiltinBoolTy;
diff --git a/clang/include/clang/AST/BuiltinTypes.def b/clang/include/clang/AST/BuiltinTypes.def
index 4eae6962a46de6..444be4311a7431 100644
--- a/clang/include/clang/AST/BuiltinTypes.def
+++ b/clang/include/clang/AST/BuiltinTypes.def
@@ -314,9 +314,6 @@ PLACEHOLDER_TYPE(UnknownAny, UnknownAnyTy)
 
 PLACEHOLDER_TYPE(BuiltinFn, BuiltinFnTy)
 
-// A placeholder type for __builtin_counted_by_ref.
-PLACEHOLDER_TYPE(BuiltinCountedByRef, BuiltinCountedByRefTy)
-
 // The type of a cast which, in ARC, would normally require a
 // __bridge, but which might be okay depending on the immediate
 // context.
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index f4b71861968e77..fd834c14ce790f 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1111,9 +1111,6 @@ enum PredefinedTypeIDs {
   /// \brief The '__ibm128' type
   PREDEF_TYPE_IBM128_ID = 74,
 
-  /// \brief The placeholder type for __builtin_counted_by_ref.
-  PREDEF_TYPE_BUILTIN_COUNTED_BY_REF = 75,
-
 /// OpenCL image types with auto numeration
 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix)                   \
   PREDEF_TYPE_##Id##_ID,
@@ -1151,7 +1148,7 @@ enum PredefinedTypeIDs {
 ///
 /// Type IDs for non-predefined types will start at
 /// NUM_PREDEF_TYPE_IDs.
-const unsigned NUM_PREDEF_TYPE_IDS = 514;
+const unsigned NUM_PREDEF_TYPE_IDS = 513;
 
 // Ensure we do not overrun the predefined types we reserved
 // in the enum PredefinedTypeIDs above.
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 06226afaf23aab..14fbadbc35ae5d 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1390,10 +1390,6 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target,
   if (LangOpts.MatrixTypes)
     InitBuiltinType(IncompleteMatrixIdxTy, BuiltinType::IncompleteMatrixIdx);
 
-  // Placeholder for __builtin_counted_by_ref().
-  if (!LangOpts.CPlusPlus)
-    InitBuiltinType(BuiltinCountedByRefTy, BuiltinType::BuiltinCountedByRef);
-
   // Builtin types for 'id', 'Class', and 'SEL'.
   InitBuiltinType(ObjCBuiltinIdTy, BuiltinType::ObjCId);
   InitBuiltinType(ObjCBuiltinClassTy, BuiltinType::ObjCClass);
diff --git a/clang/lib/AST/NSAPI.cpp b/clang/lib/AST/NSAPI.cpp
index 6a722b89af6205..311fec32bbfa90 100644
--- a/clang/lib/AST/NSAPI.cpp
+++ b/clang/lib/AST/NSAPI.cpp
@@ -466,7 +466,6 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const {
   case BuiltinType::Half:
   case BuiltinType::PseudoObject:
   case BuiltinType::BuiltinFn:
-  case BuiltinType::BuiltinCountedByRef:
   case BuiltinType::IncompleteMatrixIdx:
   case BuiltinType::ArraySection:
   case BuiltinType::OMPArrayShaping:
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 1e44c2bd234fbb..b70f86ef31442d 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3444,8 +3444,6 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const {
     return "<ARC unbridged cast type>";
   case BuiltinFn:
     return "<builtin fn type>";
-  case BuiltinCountedByRef:
-    return "<builtin counted by ref type>";
   case ObjCId:
     return "id";
   case ObjCClass:
@@ -4863,7 +4861,6 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
 #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
 #include "clang/Basic/HLSLIntangibleTypes.def"
     case BuiltinType::BuiltinFn:
-    case BuiltinType::BuiltinCountedByRef:
     case BuiltinType::NullPtr:
     case BuiltinType::IncompleteMatrixIdx:
     case BuiltinType::ArraySection:
diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp
index 088bd5c379d8c4..fbb7fc5cd76902 100644
--- a/clang/lib/AST/TypeLoc.cpp
+++ b/clang/lib/AST/TypeLoc.cpp
@@ -433,7 +433,6 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const {
 #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
 #include "clang/Basic/HLSLIntangibleTypes.def"
   case BuiltinType::BuiltinFn:
-  case BuiltinType::BuiltinCountedByRef:
   case BuiltinType::IncompleteMatrixIdx:
   case BuiltinType::ArraySection:
   case BuiltinType::OMPArrayShaping:
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 52b47a995624f4..2d4a7cd287b70d 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -755,7 +755,6 @@ static ExprResult BuiltinDumpStruct(Sema &S, CallExpr *TheCall) {
     case BuiltinType::PseudoObject:
     case BuiltinType::UnknownAny:
     case BuiltinType::BuiltinFn:
-    case BuiltinType::BuiltinCountedByRef:
       // This might be a callable.
       break;
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index d4a90cf262b09f..695c8bafea72e6 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14692,14 +14692,11 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) {
 
   // The result of __builtin_counted_by_ref cannot be assigned to a variable.
   // It allows leaking and modification of bounds safety information.
-  if (const auto *CE = dyn_cast_if_present<CallExpr>(VD->getInit())) {
-    const Expr *E = CE->getCallee()->IgnoreParenImpCasts();
-    if (const BuiltinType *PTy = E->getType()->getAsPlaceholderType();
-        PTy && PTy->getKind() == BuiltinType::BuiltinCountedByRef)
-      Diag(E->getExprLoc(),
-           diag::err_builtin_counted_by_ref_cannot_leak_reference)
-          << E->getSourceRange();
-  }
+  if (const auto *CE = dyn_cast_if_present<CallExpr>(VD->getInit());
+      CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref)
+    Diag(CE->getExprLoc(),
+         diag::err_builtin_counted_by_ref_cannot_leak_reference)
+        << CE->getSourceRange();
 
   checkAttributesAfterMerging(*this, *VD);
 
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index f1925bae9b91e3..83b2c95c6bfba0 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -3382,9 +3382,7 @@ ExprResult Sema::BuildDeclarationNameExpr(
   case Decl::Function: {
     if (unsigned BID = cast<FunctionDecl>(VD)->getBuiltinID()) {
       if (!Context.BuiltinInfo.isDirectlyAddressable(BID)) {
-        type = (BID == Builtin::BI__builtin_counted_by_ref)
-                   ? Context.BuiltinCountedByRefTy
-                   : Context.BuiltinFnTy;
+        type = Context.BuiltinFnTy;
         valueKind = VK_PRValue;
         break;
       }
@@ -4898,15 +4896,10 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base,
 
   // We cannot use __builtin_counted_by_ref in a binary expression. It's
   // possible to leak the reference and violate bounds security.
-  Expr *E = base->IgnoreParenImpCasts();
-  if (auto *CE = dyn_cast<CallExpr>(E)) {
-    E = CE->getCallee()->IgnoreParenImpCasts();
-    if (const BuiltinType *PTy = E->getType()->getAsPlaceholderType();
-        PTy && PTy->getKind() == BuiltinType::BuiltinCountedByRef) {
-      Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
-          << 0 << E->getSourceRange();
-    }
-  }
+  if (auto *CE = dyn_cast<CallExpr>(base->IgnoreParenImpCasts());
+      CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref)
+    Diag(CE->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
+        << 0 << CE->getSourceRange();
 
   // Handle any non-overload placeholder types in the base and index
   // expressions.  We can't handle overloads here because the other
@@ -6202,7 +6195,6 @@ static bool isPlaceholderToRemoveAsArg(QualType type) {
   // These are always invalid as call arguments and should be reported.
   case BuiltinType::BoundMember:
   case BuiltinType::BuiltinFn:
-  case BuiltinType::BuiltinCountedByRef:
   case BuiltinType::IncompleteMatrixIdx:
   case BuiltinType::ArraySection:
   case BuiltinType::OMPArrayShaping:
@@ -6229,15 +6221,12 @@ bool Sema::CheckArgsForPlaceholders(MultiExprArg args) {
     // The result of __builtin_counted_by_ref cannot be used as a function
     // argument. It allows leaking and modification of bounds safety
     // information.
-    if (const auto *CE = dyn_cast<CallExpr>(args[i])) {
-      const Expr *E = CE->getCallee()->IgnoreParenImpCasts();
-      if (const BuiltinType *PTy = E->getType()->getAsPlaceholderType();
-          PTy && PTy->getKind() == BuiltinType::BuiltinCountedByRef) {
-        hasInvalid = true;
-        Diag(E->getExprLoc(),
-             diag::err_builtin_counted_by_ref_cannot_leak_reference)
-            << E->getSourceRange();
-      }
+    if (const auto *CE = dyn_cast<CallExpr>(args[i]);
+        CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref) {
+      hasInvalid = true;
+      Diag(CE->getExprLoc(),
+           diag::err_builtin_counted_by_ref_cannot_leak_reference)
+          << CE->getSourceRange();
     }
   }
 
@@ -6802,9 +6791,7 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
   ExprResult Result;
   QualType ResultTy;
   if (BuiltinID &&
-      (Fn->getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn) ||
-       Fn->getType()->isSpecificBuiltinType(
-           BuiltinType::BuiltinCountedByRef))) {
+      Fn->getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn)) {
     // Extract the return type from the (builtin) function pointer type.
     // FIXME Several builtins still have setType in
     // Sema::CheckBuiltinFunctionCall. One should review their definitions in
@@ -15242,24 +15229,21 @@ ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
 
   // We cannot use __builtin_counted_by_ref in a binary expression. It's
   // possible to leak the reference and violate bounds security.
-  auto CheckBuiltinCountedByRefPlaceholder = [&](const Expr *E) {
-    if (const auto *CE = dyn_cast<CallExpr>(E->IgnoreParenImpCasts())) {
-      E = CE->getCallee()->IgnoreParenImpCasts();
-      if (const BuiltinType *PTy = E->getType()->getAsPlaceholderType();
-          PTy && PTy->getKind() == BuiltinType::BuiltinCountedByRef) {
-        if (BinaryOperator::isAssignmentOp(Opc))
-          Diag(E->getExprLoc(),
-               diag::err_builtin_counted_by_ref_cannot_leak_reference)
-              << E->getSourceRange();
-        else
-          Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
-              << 1 << E->getSourceRange();
-      }
+  auto CheckBuiltinCountedByRef = [&](const Expr *E) {
+    if (const auto *CE = dyn_cast<CallExpr>(E->IgnoreParenImpCasts());
+        CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref) {
+      if (BinaryOperator::isAssignmentOp(Opc))
+        Diag(E->getExprLoc(),
+             diag::err_builtin_counted_by_ref_cannot_leak_reference)
+            << E->getSourceRange();
+      else
+        Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
+            << 1 << E->getSourceRange();
     }
   };
 
-  CheckBuiltinCountedByRefPlaceholder(LHSExpr);
-  CheckBuiltinCountedByRefPlaceholder(RHSExpr);
+  CheckBuiltinCountedByRef(LHSExpr);
+  CheckBuiltinCountedByRef(RHSExpr);
 
   return BuildBinOp(S, TokLoc, Opc, LHSExpr, RHSExpr);
 }
@@ -21061,13 +21045,6 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) {
     return ExprError();
   }
 
-  case BuiltinType::BuiltinCountedByRef: {
-    Diag(E->IgnoreParens()->getExprLoc(),
-         diag::err_builtin_counted_by_ref_cannot_leak_reference)
-        << E->IgnoreParens()->getSourceRange();
-    return ExprError();
-  }
-
   case BuiltinType::IncompleteMatrixIdx:
     Diag(cast<MatrixSubscriptExpr>(E->IgnoreParens())
              ->getRowIdx()
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 024dc05f6c5636..6dc5867ed71eb9 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -3765,15 +3765,11 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
         << FSI->getFirstCoroutineStmtKeyword();
   }
 
-  if (const auto *CE = dyn_cast_if_present<CallExpr>(RetVal.get())) {
-    const Expr *E = CE->getCallee()->IgnoreParenImpCasts();
-    if (const BuiltinType *PTy = E->getType()->getAsPlaceholderType();
-        PTy && PTy->getKind() == BuiltinType::BuiltinCountedByRef) {
-      Diag(E->getExprLoc(),
-           diag::err_builtin_counted_by_ref_cannot_leak_reference)
-          << E->getSourceRange();
-    }
-  }
+  if (const auto *CE = dyn_cast_if_present<CallExpr>(RetVal.get());
+      CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref)
+    Diag(CE->getExprLoc(),
+         diag::err_builtin_counted_by_ref_cannot_leak_reference)
+        << CE->getSourceRange();
 
   StmtResult R =
       BuildReturnStmt(ReturnLoc, RetVal.get(), /*AllowRecovery=*/true);
diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp
index e7f54cf8839c70..ec18e84255ca8e 100644
--- a/clang/lib/Serialization/ASTCommon.cpp
+++ b/clang/lib/Serialization/ASTCommon.cpp
@@ -274,9 +274,6 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) {
   case BuiltinType::BuiltinFn:
     ID = PREDEF_TYPE_BUILTIN_FN;
     break;
-  case BuiltinType::BuiltinCountedByRef:
-    ID = PREDEF_TYPE_BUILTIN_COUNTED_BY_REF;
-    break;
   case BuiltinType::IncompleteMatrixIdx:
     ID = PREDEF_TYPE_INCOMPLETE_MATRIX_IDX;
     break;
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 76dfaa5481e424..ec85fad3389a1c 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7458,9 +7458,6 @@ QualType ASTReader::GetType(TypeID ID) {
     case PREDEF_TYPE_BUILTIN_FN:
       T = Context.BuiltinFnTy;
       break;
-    case PREDEF_TYPE_BUILTIN_COUNTED_BY_REF:
-      T = Context.BuiltinCountedByRefTy;
-      break;
     case PREDEF_TYPE_INCOMPLETE_MATRIX_IDX:
       T = Context.IncompleteMatrixIdxTy;
       break;
diff --git a/clang/test/Modules/no-external-type-id.cppm b/clang/test/Modules/no-external-type-id.cppm
index 162300478632db..d067e574e72e37 100644
--- a/clang/test/Modules/no-external-type-id.cppm
+++ b/clang/test/Modules/no-external-type-id.cppm
@@ -23,7 +23,7 @@ export module b;
 import a;
 export int b();
 
-// CHECK: <DECL_FUNCTION {{.*}} op8=4128
+// CHECK: <DECL_FUNCTION {{.*}} op8=4120
 // CHECK: <TYPE_FUNCTION_PROTO
 
 //--- a.v1.cppm

>From b8d93b60db2a013ff2feac44b22d4011157006f8 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Tue, 19 Nov 2024 17:39:19 -0800
Subject: [PATCH 3/9] Remove removed placeholder type.

---
 lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 4e02acff3f2076..5f8163211857c3 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -4951,7 +4951,6 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type,
     case clang::BuiltinType::Kind::ARCUnbridgedCast:
     case clang::BuiltinType::Kind::BoundMember:
     case clang::BuiltinType::Kind::BuiltinFn:
-    case clang::BuiltinType::Kind::BuiltinCountedByRef:
     case clang::BuiltinType::Kind::Dependent:
     case clang::BuiltinType::Kind::OCLClkEvent:
     case clang::BuiltinType::Kind::OCLEvent:
@@ -6112,7 +6111,6 @@ uint32_t TypeSystemClang::GetNumPointeeChildren(clang::QualType type) {
     case clang::BuiltinType::ARCUnbridgedCast:
     case clang::BuiltinType::PseudoObject:
     case clang::BuiltinType::BuiltinFn:
-    case clang::BuiltinType::BuiltinCountedByRef:
     case clang::BuiltinType::ArraySection:
       return 1;
     default:

>From 74109c4c03a689fde9686cde24e23f77120d09ec Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Wed, 20 Nov 2024 13:24:59 -0800
Subject: [PATCH 4/9] Remove unused code.

---
 clang/lib/Sema/SemaExpr.cpp | 18 ++----------------
 1 file changed, 2 insertions(+), 16 deletions(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 83b2c95c6bfba0..1f4d903c4d2b80 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6212,24 +6212,10 @@ bool Sema::CheckArgsForPlaceholders(MultiExprArg args) {
   for (size_t i = 0, e = args.size(); i != e; i++) {
     if (isPlaceholderToRemoveAsArg(args[i]->getType())) {
       ExprResult result = CheckPlaceholderExpr(args[i]);
-      if (result.isInvalid())
-        hasInvalid = true;
-      else
-        args[i] = result.get();
-    }
-
-    // The result of __builtin_counted_by_ref cannot be used as a function
-    // argument. It allows leaking and modification of bounds safety
-    // information.
-    if (const auto *CE = dyn_cast<CallExpr>(args[i]);
-        CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref) {
-      hasInvalid = true;
-      Diag(CE->getExprLoc(),
-           diag::err_builtin_counted_by_ref_cannot_leak_reference)
-          << CE->getSourceRange();
+      if (result.isInvalid()) hasInvalid = true;
+      else args[i] = result.get();
     }
   }
-
   return hasInvalid;
 }
 

>From ee9e2b63a3c3cb2e7e383ec463414d553513d599 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Wed, 20 Nov 2024 13:40:04 -0800
Subject: [PATCH 5/9] Extract check for __builtin_counted_by_ref CallExpr into
 a helper method.

---
 clang/include/clang/Sema/Sema.h |  5 +++++
 clang/lib/Sema/SemaExpr.cpp     | 20 ++++++++++++++------
 clang/lib/Sema/SemaStmt.cpp     |  7 +++----
 3 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d6f3508a5243f3..8f4b6c61fd9d11 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2518,6 +2518,11 @@ class Sema final : public SemaBase {
 
   bool BuiltinNonDeterministicValue(CallExpr *TheCall);
 
+  bool IsBuiltinCountedByRef(const Expr *E) {
+    const CallExpr *CE = E ? dyn_cast<CallExpr>(E->IgnoreParenImpCasts())
+                           : nullptr;
+    return CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref;
+  }
   bool BuiltinCountedByRef(CallExpr *TheCall);
 
   // Matrix builtin handling.
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 1f4d903c4d2b80..26b951f54b3ced 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4896,10 +4896,9 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base,
 
   // We cannot use __builtin_counted_by_ref in a binary expression. It's
   // possible to leak the reference and violate bounds security.
-  if (auto *CE = dyn_cast<CallExpr>(base->IgnoreParenImpCasts());
-      CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref)
-    Diag(CE->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
-        << 0 << CE->getSourceRange();
+  if (IsBuiltinCountedByRef(base))
+    Diag(base->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
+        << 0 << base->getSourceRange();
 
   // Handle any non-overload placeholder types in the base and index
   // expressions.  We can't handle overloads here because the other
@@ -6493,6 +6492,16 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
   if (CheckArgsForPlaceholders(ArgExprs))
     return ExprError();
 
+  // The result of __builtin_counted_by_ref cannot be used as a function
+  // argument. It allows leaking and modification of bounds safety information.
+  for (const Expr *Arg : ArgExprs)
+    if (IsBuiltinCountedByRef(Arg)) {
+      Diag(Arg->getExprLoc(),
+           diag::err_builtin_counted_by_ref_cannot_leak_reference)
+          << Arg->getSourceRange();
+      return ExprError();
+    }
+
   if (getLangOpts().CPlusPlus) {
     // If this is a pseudo-destructor expression, build the call immediately.
     if (isa<CXXPseudoDestructorExpr>(Fn)) {
@@ -15216,8 +15225,7 @@ ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
   // We cannot use __builtin_counted_by_ref in a binary expression. It's
   // possible to leak the reference and violate bounds security.
   auto CheckBuiltinCountedByRef = [&](const Expr *E) {
-    if (const auto *CE = dyn_cast<CallExpr>(E->IgnoreParenImpCasts());
-        CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref) {
+    if (IsBuiltinCountedByRef(E)) {
       if (BinaryOperator::isAssignmentOp(Opc))
         Diag(E->getExprLoc(),
              diag::err_builtin_counted_by_ref_cannot_leak_reference)
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 6dc5867ed71eb9..06ca5231f61dba 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -3765,11 +3765,10 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
         << FSI->getFirstCoroutineStmtKeyword();
   }
 
-  if (const auto *CE = dyn_cast_if_present<CallExpr>(RetVal.get());
-      CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref)
-    Diag(CE->getExprLoc(),
+  if (IsBuiltinCountedByRef(RetVal.get()))
+    Diag(RetVal.get()->getExprLoc(),
          diag::err_builtin_counted_by_ref_cannot_leak_reference)
-        << CE->getSourceRange();
+        << RetVal.get()->getSourceRange();
 
   StmtResult R =
       BuildReturnStmt(ReturnLoc, RetVal.get(), /*AllowRecovery=*/true);

>From 54b277c34f5cf81c996b952a984322e168d142cf Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Wed, 20 Nov 2024 13:40:30 -0800
Subject: [PATCH 6/9] Reformat.

---
 clang/include/clang/Sema/Sema.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 8f4b6c61fd9d11..769e72fb84bd3e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2519,8 +2519,8 @@ class Sema final : public SemaBase {
   bool BuiltinNonDeterministicValue(CallExpr *TheCall);
 
   bool IsBuiltinCountedByRef(const Expr *E) {
-    const CallExpr *CE = E ? dyn_cast<CallExpr>(E->IgnoreParenImpCasts())
-                           : nullptr;
+    const CallExpr *CE =
+        E ? dyn_cast<CallExpr>(E->IgnoreParenImpCasts()) : nullptr;
     return CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref;
   }
   bool BuiltinCountedByRef(CallExpr *TheCall);

>From 8ab376a9e8cd111d930c85bbc47e6bdce5545bbe Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Wed, 20 Nov 2024 13:42:10 -0800
Subject: [PATCH 7/9] Turn into a const method.

---
 clang/include/clang/Sema/Sema.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 769e72fb84bd3e..a37e7a69140adf 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2518,7 +2518,7 @@ class Sema final : public SemaBase {
 
   bool BuiltinNonDeterministicValue(CallExpr *TheCall);
 
-  bool IsBuiltinCountedByRef(const Expr *E) {
+  bool IsBuiltinCountedByRef(const Expr *E) const {
     const CallExpr *CE =
         E ? dyn_cast<CallExpr>(E->IgnoreParenImpCasts()) : nullptr;
     return CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref;

>From 90cdf04c9ee30a92397b3a78b5b7c5fc4e239e84 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Thu, 21 Nov 2024 13:34:18 -0800
Subject: [PATCH 8/9] Use helper function.

---
 clang/lib/Sema/SemaDecl.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 695c8bafea72e6..5f272ef31ccde4 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14692,11 +14692,10 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) {
 
   // The result of __builtin_counted_by_ref cannot be assigned to a variable.
   // It allows leaking and modification of bounds safety information.
-  if (const auto *CE = dyn_cast_if_present<CallExpr>(VD->getInit());
-      CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref)
-    Diag(CE->getExprLoc(),
+  if (IsBuiltinCountedByRef(VD->getInit()))
+    Diag(VD->getInit()->getExprLoc(),
          diag::err_builtin_counted_by_ref_cannot_leak_reference)
-        << CE->getSourceRange();
+        << VD->getInit()->getSourceRange();
 
   checkAttributesAfterMerging(*this, *VD);
 

>From f34c1871a011b4aaa9c8023a745a5abe2312689b Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Wed, 27 Nov 2024 09:41:46 -0800
Subject: [PATCH 9/9] Create a separate checking function.

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  9 +++--
 clang/include/clang/Sema/Sema.h               | 16 +++++---
 clang/lib/Sema/SemaChecking.cpp               | 39 +++++++++++++++++++
 clang/lib/Sema/SemaDecl.cpp                   |  7 +---
 clang/lib/Sema/SemaExpr.cpp                   | 31 +++------------
 clang/lib/Sema/SemaStmt.cpp                   |  5 +--
 clang/test/Sema/builtin-counted-by-ref.c      | 14 +++----
 7 files changed, 70 insertions(+), 51 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 37fb44d4bf74cd..268add884bab9c 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6670,14 +6670,15 @@ def warn_counted_by_attr_elt_type_unknown_size :
 // __builtin_counted_by_ref diagnostics:
 def err_builtin_counted_by_ref_must_be_flex_array_member : Error<
   "'__builtin_counted_by_ref' argument must reference a flexible array member">;
+def err_builtin_counted_by_ref_has_side_effects : Error<
+  "'__builtin_counted_by_ref' argument cannot have side-effects">;
+
 def err_builtin_counted_by_ref_cannot_leak_reference : Error<
-  "value returned by '__builtin_counted_by_ref' cannot be assigned to a "
-  "variable, have its address taken, or passed into or returned from a function">;
+  "value returned by '__builtin_counted_by_ref' cannot be %select{assigned to a "
+  "variable|passed into a function|returned from a function}0">;
 def err_builtin_counted_by_ref_invalid_use : Error<
   "value returned by '__builtin_counted_by_ref' cannot be used in "
   "%select{an array subscript|a binary}0 expression">;
-def err_builtin_counted_by_ref_has_side_effects : Error<
-  "'__builtin_counted_by_ref' argument cannot have side-effects">;
 
 let CategoryName = "ARC Semantic Issue" in {
 
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index a37e7a69140adf..b67cfd95c27703 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2518,11 +2518,17 @@ class Sema final : public SemaBase {
 
   bool BuiltinNonDeterministicValue(CallExpr *TheCall);
 
-  bool IsBuiltinCountedByRef(const Expr *E) const {
-    const CallExpr *CE =
-        E ? dyn_cast<CallExpr>(E->IgnoreParenImpCasts()) : nullptr;
-    return CE && CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref;
-  }
+  enum BuiltinCountedByRefKind {
+    AssignmentKind,
+    InitializerKind,
+    FunctionArgKind,
+    ReturnArgKind,
+    ArraySubscriptKind,
+    BinaryExprKind,
+  };
+
+  bool CheckInvalidBuiltinCountedByRef(const Expr *E,
+                                       BuiltinCountedByRefKind K);
   bool BuiltinCountedByRef(CallExpr *TheCall);
 
   // Matrix builtin handling.
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2d4a7cd287b70d..d46aa45ec79367 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -5621,6 +5621,45 @@ bool Sema::BuiltinCountedByRef(CallExpr *TheCall) {
   return false;
 }
 
+/// The result of __builtin_counted_by_ref cannot be assigned to a variable.
+/// It allows leaking and modification of bounds safety information.
+bool Sema::CheckInvalidBuiltinCountedByRef(const Expr *E,
+                                           BuiltinCountedByRefKind K) {
+  const CallExpr *CE =
+      E ? dyn_cast<CallExpr>(E->IgnoreParenImpCasts()) : nullptr;
+  if (!CE || CE->getBuiltinCallee() != Builtin::BI__builtin_counted_by_ref)
+    return false;
+
+  switch (K) {
+  case AssignmentKind:
+  case InitializerKind:
+    Diag(E->getExprLoc(),
+         diag::err_builtin_counted_by_ref_cannot_leak_reference)
+        << 0 << E->getSourceRange();
+    break;
+  case FunctionArgKind:
+    Diag(E->getExprLoc(),
+         diag::err_builtin_counted_by_ref_cannot_leak_reference)
+        << 1 << E->getSourceRange();
+    break;
+  case ReturnArgKind:
+    Diag(E->getExprLoc(),
+         diag::err_builtin_counted_by_ref_cannot_leak_reference)
+        << 2 << E->getSourceRange();
+    break;
+  case ArraySubscriptKind:
+    Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
+        << 0 << E->getSourceRange();
+    break;
+  case BinaryExprKind:
+    Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
+        << 1 << E->getSourceRange();
+    break;
+  }
+
+  return true;
+}
+
 namespace {
 
 class UncoveredArgHandler {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 5f272ef31ccde4..a573802af16906 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14690,12 +14690,7 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) {
     }
   }
 
-  // The result of __builtin_counted_by_ref cannot be assigned to a variable.
-  // It allows leaking and modification of bounds safety information.
-  if (IsBuiltinCountedByRef(VD->getInit()))
-    Diag(VD->getInit()->getExprLoc(),
-         diag::err_builtin_counted_by_ref_cannot_leak_reference)
-        << VD->getInit()->getSourceRange();
+  CheckInvalidBuiltinCountedByRef(VD->getInit(), InitializerKind);
 
   checkAttributesAfterMerging(*this, *VD);
 
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 26b951f54b3ced..a80c5911ba9819 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4894,11 +4894,7 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base,
     return ExprError();
   }
 
-  // We cannot use __builtin_counted_by_ref in a binary expression. It's
-  // possible to leak the reference and violate bounds security.
-  if (IsBuiltinCountedByRef(base))
-    Diag(base->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
-        << 0 << base->getSourceRange();
+  CheckInvalidBuiltinCountedByRef(base, ArraySubscriptKind);
 
   // Handle any non-overload placeholder types in the base and index
   // expressions.  We can't handle overloads here because the other
@@ -6495,12 +6491,8 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
   // The result of __builtin_counted_by_ref cannot be used as a function
   // argument. It allows leaking and modification of bounds safety information.
   for (const Expr *Arg : ArgExprs)
-    if (IsBuiltinCountedByRef(Arg)) {
-      Diag(Arg->getExprLoc(),
-           diag::err_builtin_counted_by_ref_cannot_leak_reference)
-          << Arg->getSourceRange();
+    if (CheckInvalidBuiltinCountedByRef(Arg, FunctionArgKind))
       return ExprError();
-    }
 
   if (getLangOpts().CPlusPlus) {
     // If this is a pseudo-destructor expression, build the call immediately.
@@ -15222,22 +15214,11 @@ ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
   if (Kind == tok::TokenKind::slash)
     DetectPrecisionLossInComplexDivision(*this, TokLoc, LHSExpr);
 
-  // We cannot use __builtin_counted_by_ref in a binary expression. It's
-  // possible to leak the reference and violate bounds security.
-  auto CheckBuiltinCountedByRef = [&](const Expr *E) {
-    if (IsBuiltinCountedByRef(E)) {
-      if (BinaryOperator::isAssignmentOp(Opc))
-        Diag(E->getExprLoc(),
-             diag::err_builtin_counted_by_ref_cannot_leak_reference)
-            << E->getSourceRange();
-      else
-        Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
-            << 1 << E->getSourceRange();
-    }
-  };
+  BuiltinCountedByRefKind K =
+      BinaryOperator::isAssignmentOp(Opc) ? AssignmentKind : BinaryExprKind;
 
-  CheckBuiltinCountedByRef(LHSExpr);
-  CheckBuiltinCountedByRef(RHSExpr);
+  CheckInvalidBuiltinCountedByRef(LHSExpr, K);
+  CheckInvalidBuiltinCountedByRef(RHSExpr, K);
 
   return BuildBinOp(S, TokLoc, Opc, LHSExpr, RHSExpr);
 }
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 06ca5231f61dba..c7502fc52f54f7 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -3765,10 +3765,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
         << FSI->getFirstCoroutineStmtKeyword();
   }
 
-  if (IsBuiltinCountedByRef(RetVal.get()))
-    Diag(RetVal.get()->getExprLoc(),
-         diag::err_builtin_counted_by_ref_cannot_leak_reference)
-        << RetVal.get()->getSourceRange();
+  CheckInvalidBuiltinCountedByRef(RetVal.get(), ReturnArgKind);
 
   StmtResult R =
       BuildReturnStmt(ReturnLoc, RetVal.get(), /*AllowRecovery=*/true);
diff --git a/clang/test/Sema/builtin-counted-by-ref.c b/clang/test/Sema/builtin-counted-by-ref.c
index c21c7093468dde..a9f46a32230065 100644
--- a/clang/test/Sema/builtin-counted-by-ref.c
+++ b/clang/test/Sema/builtin-counted-by-ref.c
@@ -48,21 +48,21 @@ void test4(struct fam_struct *ptr, int idx) {
 }
 
 void *test5(struct fam_struct *ptr, int size, int idx) {
-  char *ref = __builtin_counted_by_ref(ptr->array);                 // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
+  char *ref = __builtin_counted_by_ref(ptr->array);                 // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable}}
   char *int_ptr;
   char *p;
 
-  ref = __builtin_counted_by_ref(ptr->array);                       // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
-  g(__builtin_counted_by_ref(ptr->array));                          // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
-  g(ref = __builtin_counted_by_ref(ptr->array));                    // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
+  ref = __builtin_counted_by_ref(ptr->array);                       // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable}}
+  g(__builtin_counted_by_ref(ptr->array));                          // expected-error {{value returned by '__builtin_counted_by_ref' cannot be passed into a function}}
+  g(ref = __builtin_counted_by_ref(ptr->array));                    // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable}}
 
-  if ((ref = __builtin_counted_by_ref(ptr->array)))                 // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
+  if ((ref = __builtin_counted_by_ref(ptr->array)))                 // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable}}
     ;
 
-  for (p = __builtin_counted_by_ref(ptr->array); p && *p; ++p)      // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
+  for (p = __builtin_counted_by_ref(ptr->array); p && *p; ++p)      // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable}}
     ;
 
-  return __builtin_counted_by_ref(ptr->array);                      // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
+  return __builtin_counted_by_ref(ptr->array);                      // expected-error {{value returned by '__builtin_counted_by_ref' cannot be returned from a function}}
 }
 
 void test6(struct fam_struct *ptr, int size, int idx) {



More information about the cfe-commits mailing list