llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: cor3ntin (cor3ntin)

<details>
<summary>Changes</summary>

bindings that would be produced by

```cpp

   auto [...p] = expr;

```

This is necessary to implement P2300 
(https://eel.is/c++draft/exec#snd.concepts-5), but can also be used to 
implement a general get&lt;N&gt; function that supports aggregates

__builtin_structured_binding_size works like sizeof in that it supports both 
type and expression arguments.

If the argument cannot be destructured, a sfinae-friendly error is produced.

A type is considered a valid tuple if `std::tuple_size_v&lt;T&gt;` is a valid 
expression, even if there is no valid `std::tuple_element` specialization or 
suitable `get` function for that type.

This is modeled as a UnaryExprOrTypeTraitExpr, but it is wrapped in a 
ConstantExpr because the structured binding size can only be established during 
sema.

---

Patch is 27.05 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/131515.diff


14 Files Affected:

- (modified) clang/docs/LanguageExtensions.rst (+30) 
- (modified) clang/docs/ReleaseNotes.rst (+3) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2) 
- (modified) clang/include/clang/Basic/TokenKinds.def (+1-1) 
- (modified) clang/include/clang/Sema/Sema.h (+2-1) 
- (modified) clang/lib/AST/ByteCode/Compiler.cpp (+2) 
- (modified) clang/lib/AST/ExprConstant.cpp (+5) 
- (modified) clang/lib/AST/ItaniumMangle.cpp (+9) 
- (modified) clang/lib/Parse/ParseExpr.cpp (+10-3) 
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+93-30) 
- (modified) clang/lib/Sema/SemaExpr.cpp (+62-7) 
- (modified) clang/test/CodeGenCXX/builtins.cpp (+6) 
- (added) clang/test/CodeGenCXX/mangle-structured-binding-size.cpp (+12) 
- (added) clang/test/SemaCXX/builtin-structured-binding-size.cpp (+168) 


``````````diff
diff --git a/clang/docs/LanguageExtensions.rst 
b/clang/docs/LanguageExtensions.rst
index cc12ff5bad353..9a5cd8f1e5f5d 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -434,6 +434,36 @@ __datasizeof
 ``__datasizeof`` behaves like ``sizeof``, except that it returns the size of 
the
 type ignoring tail padding.
 
+.. _builtin_structured_binding_size-doc:
+
+__builtin_structured_binding_size (C++)
+---------------------------------------
+``__builtin_structured_binding_size`` returns the *structured binding size*
+([dcl.struct.bind]) of the type ``T`` (or unevaluate expression ``arg``)
+passed as argument.
+
+This is equivalent to the size of the pack ``p`` in ``auto&& [...p] = arg;``.
+If the argument is not destructurable (ie not an array, vector, complex,
+*tuple-like* type or destructurable class type), 
``__builtin_structured_binding_size(T)``
+is not a valid expression (``__builtin_structured_binding_size`` is 
SFINEA-friendly).
+
+A type is considered a valid tuple if ``std::tuple_size_v<T>`` is a valid 
expression,
+even if there is no valid ``std::tuple_element`` specialization or suitable
+``get`` function for that type.
+
+.. code-block:: c++
+
+  template<std::size_t Idx, typename T>
+  requires (Idx < __builtin_structured_binding_size(T))
+  decltype(auto) constexpr get_binding(T&& obj) {
+      auto && [...p] = std::forward<T>(obj);
+      return p...[Idx];
+  }
+  struct S { int a = 0, b = 42; };
+  static_assert(__builtin_structured_binding_size(S) == 2);
+  static_assert(get_binding<1>(S{}) == 42);
+
+
 _BitInt, _ExtInt
 ----------------
 
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2a1c5ee2d788e..f49e389773e4e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -74,6 +74,9 @@ What's New in Clang |release|?
 C++ Language Changes
 --------------------
 
+- Added a :ref:`__builtin_structured_binding_size 
<builtin_structured_binding_size-doc>` (T)
+  builtin that returns the number of structured bindings that would be 
produced by destructuring ``T``.
+
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 86c9c955c1c78..fad826c1c6336 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -591,6 +591,8 @@ def err_decomp_decl_std_tuple_size_not_constant : Error<
   "is not a valid integral constant expression">;
 def note_in_binding_decl_init : Note<
   "in implicit initialization of binding declaration %0">;
+def err_arg_is_not_destructurable : Error<
+  "type %0 is not destructurable">;
 
 def err_std_type_trait_not_class_template : Error<
   "unsupported standard library implementation: "
diff --git a/clang/include/clang/Basic/TokenKinds.def 
b/clang/include/clang/Basic/TokenKinds.def
index 397a5d95709fb..bad9387673ef9 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -553,8 +553,8 @@ TYPE_TRAIT_2(__reference_converts_from_temporary, 
ReferenceConvertsFromTemporary
 // IsDeducible is only used internally by clang for CTAD implementation and
 // is not exposed to users.
 TYPE_TRAIT_2(/*EmptySpellingName*/, IsDeducible, KEYCXX)
-
 TYPE_TRAIT_1(__is_bitwise_cloneable, IsBitwiseCloneable, KEYALL)
+UNARY_EXPR_OR_TYPE_TRAIT(__builtin_structured_binding_size, 
StructuredBindingSize, KEYCXX)
 
 // Embarcadero Expression Traits
 EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 657350fa843b9..cd7078b119712 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6095,7 +6095,8 @@ class Sema final : public SemaBase {
                                          RecordDecl *ClassDecl,
                                          const IdentifierInfo *Name);
 
-  unsigned GetDecompositionElementCount(QualType DecompType);
+  std::optional<unsigned int> GetDecompositionElementCount(QualType DecompType,
+                                                           SourceLocation Loc);
   void CheckCompleteDecompositionDeclaration(DecompositionDecl *DD);
 
   /// Stack containing information needed when in C++2a an 'auto' is 
encountered
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index b9f88230007b5..0259605086b21 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -2154,6 +2154,8 @@ bool Compiler<Emitter>::VisitUnaryExprOrTypeTraitExpr(
             E->getArgumentType()),
         E);
   }
+  assert(Kind != UETT_StructuredBindingSize &&
+         "should have been evaluated in Sema");
 
   return false;
 }
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f8e8aaddbfdbd..1763bbc18043d 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -14878,6 +14878,11 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr(
     }
     return Success(Sizeof, E);
   }
+  case UETT_StructuredBindingSize:
+    // This can only be computed from Sema and has been cached.
+    // We can still get there from code that strips the outer ConstantExpr.
+    return false;
+
   case UETT_OpenMPRequiredSimdAlign:
     assert(E->isArgumentType());
     return Success(
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index b6ba36784f38a..12993d5cb35f1 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -5389,6 +5389,15 @@ void CXXNameMangler::mangleExpression(const Expr *E, 
unsigned Arity,
       Diags.Report(DiagID);
       return;
     }
+    case UETT_StructuredBindingSize:
+      Out << "u11__builtin_structured_binding_size";
+      if (SAE->isArgumentType())
+        mangleType(SAE->getArgumentType());
+      else
+        mangleTemplateArgExpr(SAE->getArgumentExpr());
+      Out << 'E';
+      break;
+      return;
     }
     break;
   }
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 0c28972d6ed8f..2e3f0ce3194f5 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1544,6 +1544,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind 
ParseKind,
   // unary-expression: '__builtin_omp_required_simd_align' '(' type-name ')'
   case tok::kw___builtin_omp_required_simd_align:
   case tok::kw___builtin_vectorelements:
+  case tok::kw___builtin_structured_binding_size:
     if (NotPrimaryExpression)
       *NotPrimaryExpression = true;
     AllowSuffix = false;
@@ -2463,7 +2464,8 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token 
&OpTok,
                        tok::kw___datasizeof, tok::kw___alignof, 
tok::kw_alignof,
                        tok::kw__Alignof, tok::kw_vec_step,
                        tok::kw___builtin_omp_required_simd_align,
-                       tok::kw___builtin_vectorelements) &&
+                       tok::kw___builtin_vectorelements,
+                       tok::kw___builtin_structured_binding_size) &&
          "Not a typeof/sizeof/alignof/vec_step expression!");
 
   ExprResult Operand;
@@ -2473,7 +2475,8 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token 
&OpTok,
     // If construct allows a form without parenthesis, user may forget to put
     // pathenthesis around type name.
     if (OpTok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof,
-                      tok::kw_alignof, tok::kw__Alignof)) {
+                      tok::kw_alignof, tok::kw__Alignof,
+                      tok::kw___builtin_structured_binding_size)) {
       if (isTypeIdUnambiguously()) {
         DeclSpec DS(AttrFactory);
         ParseSpecifierQualifierList(DS);
@@ -2599,7 +2602,8 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
   assert(Tok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof,
                      tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step,
                      tok::kw___builtin_omp_required_simd_align,
-                     tok::kw___builtin_vectorelements) &&
+                     tok::kw___builtin_vectorelements,
+                     tok::kw___builtin_structured_binding_size) &&
          "Not a sizeof/alignof/vec_step expression!");
   Token OpTok = Tok;
   ConsumeToken();
@@ -2687,6 +2691,9 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
   case tok::kw___datasizeof:
     ExprKind = UETT_DataSizeOf;
     break;
+  case tok::kw___builtin_structured_binding_size:
+    ExprKind = UETT_StructuredBindingSize;
+    break;
   case tok::kw___builtin_vectorelements:
     ExprKind = UETT_VectorElements;
     break;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index a02bd8335fa20..164e81e1cfa61 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1475,6 +1475,48 @@ static DeclAccessPair findDecomposableBaseClass(Sema &S, 
SourceLocation Loc,
   return DeclAccessPair::make(const_cast<CXXRecordDecl*>(ClassWithFields), AS);
 }
 
+static bool CheckMemberDecompositionFields(Sema &S, SourceLocation Loc,
+                                           const CXXRecordDecl *OrigRD,
+                                           QualType DecompType,
+                                           DeclAccessPair BasePair) {
+  const CXXRecordDecl *RD = cast_or_null<CXXRecordDecl>(BasePair.getDecl());
+  if (!RD)
+    return true;
+
+  for (auto *FD : RD->fields()) {
+    if (FD->isUnnamedBitField())
+      continue;
+
+    // All the non-static data members are required to be nameable, so they
+    // must all have names.
+    if (!FD->getDeclName()) {
+      if (RD->isLambda()) {
+        S.Diag(Loc, diag::err_decomp_decl_lambda);
+        S.Diag(RD->getLocation(), diag::note_lambda_decl);
+        return true;
+      }
+
+      if (FD->isAnonymousStructOrUnion()) {
+        S.Diag(Loc, diag::err_decomp_decl_anon_union_member)
+            << DecompType << FD->getType()->isUnionType();
+        S.Diag(FD->getLocation(), diag::note_declared_at);
+        return true;
+      }
+
+      // FIXME: Are there any other ways we could have an anonymous member?
+    }
+    // The field must be accessible in the context of the structured binding.
+    // We already checked that the base class is accessible.
+    // FIXME: Add 'const' to AccessedEntity's classes so we can remove the
+    // const_cast here.
+    S.CheckStructuredBindingMemberAccess(
+        Loc, const_cast<CXXRecordDecl *>(OrigRD),
+        DeclAccessPair::make(FD, CXXRecordDecl::MergeAccess(
+                                     BasePair.getAccess(), FD->getAccess())));
+  }
+  return false;
+}
+
 static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings,
                                      ValueDecl *Src, QualType DecompType,
                                      const CXXRecordDecl *OrigRD) {
@@ -1503,43 +1545,20 @@ static bool checkMemberDecomposition(Sema &S, 
ArrayRef<BindingDecl*> Bindings,
   auto FlatBindings = DD->flat_bindings();
   assert(llvm::range_size(FlatBindings) == NumFields);
   auto FlatBindingsItr = FlatBindings.begin();
+
+  if (CheckMemberDecompositionFields(S, Src->getLocation(), OrigRD, DecompType,
+                                     BasePair))
+    return true;
+
   for (auto *FD : RD->fields()) {
     if (FD->isUnnamedBitField())
       continue;
 
-    // All the non-static data members are required to be nameable, so they
-    // must all have names.
-    if (!FD->getDeclName()) {
-      if (RD->isLambda()) {
-        S.Diag(Src->getLocation(), diag::err_decomp_decl_lambda);
-        S.Diag(RD->getLocation(), diag::note_lambda_decl);
-        return true;
-      }
-
-      if (FD->isAnonymousStructOrUnion()) {
-        S.Diag(Src->getLocation(), diag::err_decomp_decl_anon_union_member)
-          << DecompType << FD->getType()->isUnionType();
-        S.Diag(FD->getLocation(), diag::note_declared_at);
-        return true;
-      }
-
-      // FIXME: Are there any other ways we could have an anonymous member?
-    }
-
     // We have a real field to bind.
     assert(FlatBindingsItr != FlatBindings.end());
     BindingDecl *B = *(FlatBindingsItr++);
     SourceLocation Loc = B->getLocation();
 
-    // The field must be accessible in the context of the structured binding.
-    // We already checked that the base class is accessible.
-    // FIXME: Add 'const' to AccessedEntity's classes so we can remove the
-    // const_cast here.
-    S.CheckStructuredBindingMemberAccess(
-        Loc, const_cast<CXXRecordDecl *>(OrigRD),
-        DeclAccessPair::make(FD, CXXRecordDecl::MergeAccess(
-                                     BasePair.getAccess(), FD->getAccess())));
-
     // Initialize the binding to Src.FD.
     ExprResult E = S.BuildDeclRefExpr(Src, DecompType, VK_LValue, Loc);
     if (E.isInvalid())
@@ -1642,6 +1661,50 @@ void 
Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) {
     DD->setInvalidDecl();
 }
 
+std::optional<unsigned> Sema::GetDecompositionElementCount(QualType T,
+                                                           SourceLocation Loc) 
{
+  const ASTContext &Ctx = getASTContext();
+  assert(!T->isDependentType());
+  if (auto *CAT = Ctx.getAsConstantArrayType(T))
+    return CAT->getSize().getZExtValue();
+  if (auto *VT = T->getAs<VectorType>())
+    return VT->getNumElements();
+  if (T->getAs<ComplexType>())
+    return 2;
+
+  llvm::APSInt TupleSize(Ctx.getTypeSize(Ctx.getSizeType()));
+  switch (isTupleLike(*this, Loc, T, TupleSize)) {
+  case IsTupleLike::Error:
+    return {};
+  case IsTupleLike::TupleLike:
+    return TupleSize.getExtValue();
+  case IsTupleLike::NotTupleLike:
+    break;
+  }
+  CXXRecordDecl *OrigRD = T->getAsCXXRecordDecl();
+  if (!OrigRD || OrigRD->isUnion()) {
+    return std::nullopt;
+  }
+
+  if (RequireCompleteType(Loc, T, diag::err_incomplete_type))
+    return std::nullopt;
+
+  CXXCastPath BasePath;
+  DeclAccessPair BasePair =
+      findDecomposableBaseClass(*this, Loc, OrigRD, BasePath);
+  const CXXRecordDecl *RD = cast_or_null<CXXRecordDecl>(BasePair.getDecl());
+  if (!RD)
+    return std::nullopt;
+
+  unsigned NumFields = llvm::count_if(
+      RD->fields(), [](FieldDecl *FD) { return !FD->isUnnamedBitField(); });
+
+  if (CheckMemberDecompositionFields(*this, Loc, OrigRD, T, BasePair))
+    return true;
+
+  return NumFields;
+}
+
 void Sema::MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old) {
   // Shortcut if exceptions are disabled.
   if (!getLangOpts().CXXExceptions)
@@ -17262,8 +17325,8 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) {
       Expr::EvalResult Result;
       SmallString<12> ValueString;
       bool Print;
-    } DiagSide[2] = {{LHS, Expr::EvalResult(), {}, false},
-                     {RHS, Expr::EvalResult(), {}, false}};
+    } DiagSide[2] = {{Op->getLHS(), Expr::EvalResult(), {}, false},
+                     {Op->getRHS(), Expr::EvalResult(), {}, false}};
     for (unsigned I = 0; I < 2; I++) {
       const Expr *Side = DiagSide[I].Cond;
 
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e19136b394800..8766331a0df59 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4160,6 +4160,54 @@ static bool CheckVecStepTraitOperandType(Sema &S, 
QualType T,
   return false;
 }
 
+static ExprResult BuildStructuredBindingSizeTraitImpl(Sema &S, QualType T,
+                                                      Expr *E,
+                                                      TypeSourceInfo *TInfo,
+                                                      SourceLocation Loc,
+                                                      SourceRange ArgRange) {
+  assert(!!E != !!TInfo);
+  assert(!T->isDependentType());
+  std::optional<unsigned> Size =
+      S.GetDecompositionElementCount(T, ArgRange.getBegin());
+  if (!Size) {
+    return S.Diag(Loc, diag::err_arg_is_not_destructurable) << T << ArgRange;
+    return ExprError();
+  }
+  Expr *Inner;
+  if (E)
+    Inner = new (S.getASTContext()) UnaryExprOrTypeTraitExpr(
+        UnaryExprOrTypeTrait::UETT_StructuredBindingSize, E,
+        S.getASTContext().getSizeType(), Loc, E->getEndLoc());
+
+  else
+    Inner = new (S.getASTContext()) UnaryExprOrTypeTraitExpr(
+        UnaryExprOrTypeTrait::UETT_StructuredBindingSize, TInfo,
+        S.getASTContext().getSizeType(), Loc, ArgRange.getEnd());
+
+  // Computing the number of bindings requires Sema and is non-trivial,
+  // so we cache the result now.
+  llvm::APSInt V =
+      S.getASTContext().MakeIntValue(*Size, S.getASTContext().getSizeType());
+  return ConstantExpr::Create(S.getASTContext(), Inner, APValue{V});
+}
+
+static ExprResult BuildStructuredBindingSizeTrait(Sema &S,
+                                                  TypeSourceInfo *TInfo,
+                                                  SourceLocation Loc,
+                                                  SourceRange ArgRange) {
+  return BuildStructuredBindingSizeTraitImpl(S, TInfo->getType(),
+                                             /*Expr=*/nullptr, TInfo, Loc,
+                                             ArgRange);
+}
+
+static ExprResult BuildStructuredBindingSizeTrait(Sema &S, SourceLocation 
OpLoc,
+                                                  Expr *E) {
+
+  return BuildStructuredBindingSizeTraitImpl(S, E->getType(), E,
+                                             /*TInfo=*/nullptr, OpLoc,
+                                             E->getSourceRange());
+}
+
 static bool CheckVectorElementsTraitOperandType(Sema &S, QualType T,
                                                 SourceLocation Loc,
                                                 SourceRange ArgRange) {
@@ -4650,10 +4698,14 @@ ExprResult 
Sema::CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo,
 
   QualType T = TInfo->getType();
 
-  if (!T->isDependentType() &&
-      CheckUnaryExprOrTypeTraitOperand(T, OpLoc, R, ExprKind,
-                                       getTraitSpelling(ExprKind)))
-    return ExprError();
+  if (!T->isDependentType()) {
+    if (ExprKind == UETT_StructuredBindingSize)
+      return BuildStructuredBindingSizeTrait(*this, TInfo, OpLoc, R);
+
+    if (CheckUnaryExprOrTypeTraitOperand(T, OpLoc, R, ExprKind,
+                                         getTraitSpelling(ExprKind)))
+      return ExprError();
+  }
 
   // Adds overload of TransformToPotentiallyEvaluated for TypeSourceInfo to
   // properly deal with VLAs in nested calls of sizeof and typeof.
@@ -4680,14 +4732,17 @@ Sema::CreateUnaryExprOrTypeTraitExpr(Expr *E, 
SourceLocation OpLoc,
   bool isInvalid = false;
   if (E->isTypeDependent()) {
     // Delay type-checking for type-dependent expressions.
+  } else if (ExprKind == UETT_StructuredBindingSize) {
+    // Custom logic
+    return BuildStructuredBindingSizeTrait(*this, OpLoc, E);
   } else if (ExprKind == UETT_AlignOf || ExprKind == UETT_PreferredAlignOf) {
     isInvalid = CheckAlignOfExpr(*this, E, ExprKind);
   } else if (ExprKind == UETT_VecStep) {
     isInvalid = CheckVecStepExpr(E);
   } else if (ExprKind == UETT_OpenMPRequiredSimdAlign) {
-      Diag(E->getExprLoc(), diag::err_openmp_default_simd_align_expr);
-      isInvalid = true;
-  } else if (E->refersToBitField()) {  // C99 6.5.3.4p1.
+    Diag(E->getExprLoc(), diag::err_openmp_default_simd_align_expr);
+    isInvalid = true;
+  } else if (E->refersToBitField()) { // C99 6.5.3.4p1.
     Diag(E->getExprLoc(), diag::err_sizeof_alignof_typeof_bitfield) << 0;
     isInvalid = true;
   } else if (ExprKind == UETT_VectorElements) {
diff --git a/clang/test/CodeGenCXX/builtins.cpp 
b/clang/test/CodeGenCXX/builtins.cpp
index 37f9491d12d04..9169f3a3276d3 100644
--- a/clang/test/CodeGenCXX/builtins.cpp
+++ b/clang/test/CodeGenCXX/builtins.cpp
@@ -77,3 +77,9 @@ int constexpr_overflow_result() {
   // CHECK: [[RET_VAL:%.+]] = load i32, ptr [[Z]]
   // CHECK: ret i32 [[RET_VAL]]
 }
+
+int structured_binding_size() {
+  struct S2 {int a, b;};
+  return __builtin_structured_binding_size(S2);
+  // CHECK: ret i32 2
+}
diff --git a/clang/test/CodeGenCXX/mangle-structured-binding-size.cpp 
b/clang/test/CodeGenCXX/mangle-structured-binding-size.cpp
new file mode 100644
index 0000000000000..5b53ed8d7166d
--- /dev/null
+++ b/clang/test/CodeGenCXX/mangle-structured-binding-si...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/131515
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to