Skip to content

Commit fd41300

Browse files
authored
Merge branch 'main' into mcinally/native_test
2 parents 1c0180e + 19659ee commit fd41300

File tree

67 files changed

+1527
-1485
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+1527
-1485
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ Improvements to Clang's diagnostics
271271
Moved the warning for a missing (though implied) attribute on a redeclaration into this group.
272272
Added a new warning in this group for the case where the attribute is missing/implicit on
273273
an override of a virtual method.
274+
- Implemented diagnostics when retrieving the tuple size for types where its specialization of `std::tuple_size`
275+
produces an invalid size (either negative or greater than the implementation limit). (#GH159563)
274276
- Fixed fix-it hint for fold expressions. Clang now correctly places the suggested right
275277
parenthesis when diagnosing malformed fold expressions. (#GH151787)
276278
- Added fix-it hint for when scoped enumerations require explicit conversions for binary operations. (#GH24265)
@@ -356,8 +358,8 @@ Bug Fixes in This Version
356358
and vector of 4 ``float`` values. (#GH155405)
357359
- Fixed inconsistent shadow warnings for lambda capture of structured bindings.
358360
Previously, ``[val = val]`` (regular parameter) produced no warnings with ``-Wshadow``
359-
while ``[a = a]`` (where ``a`` is from ``auto [a, b] = std::make_pair(1, 2)``)
360-
incorrectly produced warnings. Both cases now consistently show no warnings with
361+
while ``[a = a]`` (where ``a`` is from ``auto [a, b] = std::make_pair(1, 2)``)
362+
incorrectly produced warnings. Both cases now consistently show no warnings with
361363
``-Wshadow`` and show uncaptured-local warnings with ``-Wshadow-all``. (#GH68605)
362364
- Fixed a failed assertion with a negative limit parameter value inside of
363365
``__has_embed``. (#GH157842)

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,9 @@ def err_decomp_decl_std_tuple_element_not_specialized : Error<
638638
def err_decomp_decl_std_tuple_size_not_constant : Error<
639639
"cannot decompose this type; 'std::tuple_size<%0>::value' "
640640
"is not a valid integral constant expression">;
641+
def err_decomp_decl_std_tuple_size_invalid
642+
: Error<"cannot decompose this type; 'std::tuple_size<%0>::value' "
643+
"is not a valid size: %1">;
641644
def note_in_binding_decl_init : Note<
642645
"in implicit initialization of binding declaration %0">;
643646
def err_arg_is_not_destructurable : Error<

clang/lib/AST/ExprClassification.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,13 @@ static Cl::Kinds ClassifyMemberExpr(ASTContext &Ctx, const MemberExpr *E) {
601601
static Cl::Kinds ClassifyBinaryOp(ASTContext &Ctx, const BinaryOperator *E) {
602602
assert(Ctx.getLangOpts().CPlusPlus &&
603603
"This is only relevant for C++.");
604+
605+
// For binary operators which are unknown due to type dependence, the
606+
// convention is to classify them as a prvalue. This does not matter much, but
607+
// it needs to agree with how they are created.
608+
if (E->getType() == Ctx.DependentTy)
609+
return Cl::CL_PRValue;
610+
604611
// C++ [expr.ass]p1: All [...] return an lvalue referring to the left operand.
605612
// Except we override this for writes to ObjC properties.
606613
if (E->isAssignmentOp())

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,8 +1063,20 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
10631063

10641064
mlir::Value VisitBinLAnd(const clang::BinaryOperator *e) {
10651065
if (e->getType()->isVectorType()) {
1066-
assert(!cir::MissingFeatures::vectorType());
1067-
return {};
1066+
mlir::Location loc = cgf.getLoc(e->getExprLoc());
1067+
auto vecTy = mlir::cast<cir::VectorType>(cgf.convertType(e->getType()));
1068+
mlir::Value zeroValue = builder.getNullValue(vecTy.getElementType(), loc);
1069+
SmallVector<mlir::Value, 16> elements(vecTy.getSize(), zeroValue);
1070+
auto zeroVec = cir::VecCreateOp::create(builder, loc, vecTy, elements);
1071+
1072+
mlir::Value lhs = Visit(e->getLHS());
1073+
mlir::Value rhs = Visit(e->getRHS());
1074+
1075+
auto cmpOpKind = cir::CmpOpKind::ne;
1076+
lhs = cir::VecCmpOp::create(builder, loc, vecTy, cmpOpKind, lhs, zeroVec);
1077+
rhs = cir::VecCmpOp::create(builder, loc, vecTy, cmpOpKind, rhs, zeroVec);
1078+
mlir::Value vecOr = builder.createAnd(loc, lhs, rhs);
1079+
return builder.createIntCast(vecOr, vecTy);
10681080
}
10691081

10701082
assert(!cir::MissingFeatures::instrumentation());

clang/lib/Driver/ToolChains/UEFI.cpp

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,29 +57,44 @@ void tools::uefi::Linker::ConstructJob(Compilation &C, const JobAction &JA,
5757
assert((Output.isFilename() || Output.isNothing()) && "invalid output");
5858
if (Output.isFilename())
5959
CmdArgs.push_back(
60-
Args.MakeArgString(std::string("-out:") + Output.getFilename()));
60+
Args.MakeArgString(std::string("/out:") + Output.getFilename()));
6161

62-
CmdArgs.push_back("-nologo");
63-
64-
// TODO: Other UEFI binary subsystems that are currently unsupported:
65-
// efi_boot_service_driver, efi_rom, efi_runtime_driver.
66-
CmdArgs.push_back("-subsystem:efi_application");
62+
CmdArgs.push_back("/nologo");
6763

6864
// Default entry function name according to the TianoCore reference
69-
// implementation is EfiMain.
70-
// TODO: Provide a flag to override the entry function name.
71-
CmdArgs.push_back("-entry:EfiMain");
65+
// implementation is EfiMain. -Wl,/subsystem:... or -Wl,/entry:... can
66+
// override these since they will be added later in AddLinkerInputs.
67+
CmdArgs.push_back("/subsystem:efi_application");
68+
CmdArgs.push_back("/entry:EfiMain");
7269

7370
// "Terminal Service Aware" flag is not needed for UEFI applications.
74-
CmdArgs.push_back("-tsaware:no");
71+
CmdArgs.push_back("/tsaware:no");
7572

7673
if (Args.hasArg(options::OPT_g_Group, options::OPT__SLASH_Z7))
77-
CmdArgs.push_back("-debug");
74+
CmdArgs.push_back("/debug");
7875

7976
Args.AddAllArgValues(CmdArgs, options::OPT__SLASH_link);
8077

8178
AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA);
8279

80+
// Sample these options first so they are claimed even under -nostdlib et al.
81+
bool NoLibc = Args.hasArg(options::OPT_nolibc);
82+
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs,
83+
options::OPT_r)) {
84+
addSanitizerRuntimes(TC, Args, CmdArgs);
85+
86+
addXRayRuntime(TC, Args, CmdArgs);
87+
88+
TC.addProfileRTLibs(Args, CmdArgs);
89+
90+
// TODO: When compiler-rt/lib/builtins is ready, enable this call:
91+
// AddRunTimeLibs(TC, TC.getDriver(), CmdArgs, Args);
92+
93+
if (!NoLibc) {
94+
// TODO: When there is a libc ready, add it here.
95+
}
96+
}
97+
8398
// This should ideally be handled by ToolChain::GetLinkerPath but we need
8499
// to special case some linker paths. In the case of lld, we need to
85100
// translate 'lld' into 'lld-link'.

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,7 +1177,7 @@ getTrivialTypeTemplateArgument(Sema &S, SourceLocation Loc, QualType T) {
11771177
namespace { enum class IsTupleLike { TupleLike, NotTupleLike, Error }; }
11781178

11791179
static IsTupleLike isTupleLike(Sema &S, SourceLocation Loc, QualType T,
1180-
llvm::APSInt &Size) {
1180+
unsigned &OutSize) {
11811181
EnterExpressionEvaluationContext ContextRAII(
11821182
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
11831183

@@ -1218,10 +1218,24 @@ static IsTupleLike isTupleLike(Sema &S, SourceLocation Loc, QualType T,
12181218
if (E.isInvalid())
12191219
return IsTupleLike::Error;
12201220

1221+
llvm::APSInt Size;
12211222
E = S.VerifyIntegerConstantExpression(E.get(), &Size, Diagnoser);
12221223
if (E.isInvalid())
12231224
return IsTupleLike::Error;
12241225

1226+
// The implementation limit is UINT_MAX-1, to allow this to be passed down on
1227+
// an UnsignedOrNone.
1228+
if (Size < 0 || Size >= UINT_MAX) {
1229+
llvm::SmallVector<char, 16> Str;
1230+
Size.toString(Str);
1231+
S.Diag(Loc, diag::err_decomp_decl_std_tuple_size_invalid)
1232+
<< printTemplateArgs(S.Context.getPrintingPolicy(), Args,
1233+
/*Params=*/nullptr)
1234+
<< StringRef(Str.data(), Str.size());
1235+
return IsTupleLike::Error;
1236+
}
1237+
1238+
OutSize = Size.getExtValue();
12251239
return IsTupleLike::TupleLike;
12261240
}
12271241

@@ -1279,9 +1293,8 @@ struct InitializingBinding {
12791293
static bool checkTupleLikeDecomposition(Sema &S,
12801294
ArrayRef<BindingDecl *> Bindings,
12811295
VarDecl *Src, QualType DecompType,
1282-
const llvm::APSInt &TupleSize) {
1296+
unsigned NumElems) {
12831297
auto *DD = cast<DecompositionDecl>(Src);
1284-
unsigned NumElems = (unsigned)TupleSize.getLimitedValue(UINT_MAX);
12851298
if (CheckBindingsCount(S, DD, DecompType, Bindings, NumElems))
12861299
return true;
12871300

@@ -1641,7 +1654,7 @@ void Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) {
16411654
// C++1z [dcl.decomp]/3:
16421655
// if the expression std::tuple_size<E>::value is a well-formed integral
16431656
// constant expression, [...]
1644-
llvm::APSInt TupleSize(32);
1657+
unsigned TupleSize;
16451658
switch (isTupleLike(*this, DD->getLocation(), DecompType, TupleSize)) {
16461659
case IsTupleLike::Error:
16471660
DD->setInvalidDecl();
@@ -1690,12 +1703,12 @@ UnsignedOrNone Sema::GetDecompositionElementCount(QualType T,
16901703
if (T->getAs<ComplexType>())
16911704
return 2u;
16921705

1693-
llvm::APSInt TupleSize(Ctx.getTypeSize(Ctx.getSizeType()));
1706+
unsigned TupleSize;
16941707
switch (isTupleLike(*this, Loc, T, TupleSize)) {
16951708
case IsTupleLike::Error:
16961709
return std::nullopt;
16971710
case IsTupleLike::TupleLike:
1698-
return static_cast<unsigned>(TupleSize.getExtValue());
1711+
return TupleSize;
16991712
case IsTupleLike::NotTupleLike:
17001713
break;
17011714
}

clang/test/CIR/CodeGen/vector-ext.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,3 +1216,43 @@ void foo22() {
12161216
// OGCG: %[[VEC_OR:.*]] = or <4 x i1> %[[NE_A_ZERO]], %[[NE_B_ZERO]]
12171217
// OGCG: %[[RESULT:.*]] = sext <4 x i1> %[[VEC_OR]] to <4 x i32>
12181218
// OGCG: store <4 x i32> %[[RESULT]], ptr %[[C_ADDR]], align 16
1219+
1220+
void foo23() {
1221+
vi4 a;
1222+
vi4 b;
1223+
vi4 c = a && b;
1224+
}
1225+
1226+
// CIR: %[[A_ADDR:.*]] = cir.alloca !cir.vector<4 x !s32i>, !cir.ptr<!cir.vector<4 x !s32i>>, ["a"]
1227+
// CIR: %[[B_ADDR:.*]] = cir.alloca !cir.vector<4 x !s32i>, !cir.ptr<!cir.vector<4 x !s32i>>, ["b"]
1228+
// CIR: %[[C_ADDR:.*]] = cir.alloca !cir.vector<4 x !s32i>, !cir.ptr<!cir.vector<4 x !s32i>>, ["c", init]
1229+
// CIR: %[[ZERO_VEC:.*]] = cir.const #cir.const_vector<[#cir.int<0> : !s32i, #cir.int<0> : !s32i, #cir.int<0> : !s32i, #cir.int<0> : !s32i]> : !cir.vector<4 x !s32i>
1230+
// CIR: %[[TMP_A:.*]] = cir.load{{.*}} %[[A_ADDR]] : !cir.ptr<!cir.vector<4 x !s32i>>, !cir.vector<4 x !s32i>
1231+
// CIR: %[[TMP_B:.*]] = cir.load{{.*}} %[[B_ADDR]] : !cir.ptr<!cir.vector<4 x !s32i>>, !cir.vector<4 x !s32i>
1232+
// CIR: %[[NE_A_ZERO:.*]] = cir.vec.cmp(ne, %[[TMP_A]], %[[ZERO_VEC]]) : !cir.vector<4 x !s32i>, !cir.vector<4 x !s32i>
1233+
// CIR: %[[NE_B_ZERO:.*]] = cir.vec.cmp(ne, %[[TMP_B]], %[[ZERO_VEC]]) : !cir.vector<4 x !s32i>, !cir.vector<4 x !s32i>
1234+
// CIR: %[[RESULT:.*]] = cir.binop(and, %[[NE_A_ZERO]], %[[NE_B_ZERO]]) : !cir.vector<4 x !s32i>
1235+
// CIR: cir.store{{.*}} %[[RESULT]], %[[C_ADDR]] : !cir.vector<4 x !s32i>, !cir.ptr<!cir.vector<4 x !s32i>>
1236+
1237+
// LLVM: %[[A_ADDR:.*]] = alloca <4 x i32>, i64 1, align 16
1238+
// LLVM: %[[B_ADDR:.*]] = alloca <4 x i32>, i64 1, align 16
1239+
// LLVM: %[[C_ADDR:.*]] = alloca <4 x i32>, i64 1, align 16
1240+
// LLVM: %[[TMP_A:.*]] = load <4 x i32>, ptr %[[A_ADDR]], align 16
1241+
// LLVM: %[[TMP_B:.*]] = load <4 x i32>, ptr %[[B_ADDR]], align 16
1242+
// LLVM: %[[NE_A_ZERO:.*]] = icmp ne <4 x i32> %[[TMP_A]], zeroinitializer
1243+
// LLVM: %[[NE_A_ZERO_SEXT:.*]] = sext <4 x i1> %[[NE_A_ZERO]] to <4 x i32>
1244+
// LLVM: %[[NE_B_ZERO:.*]] = icmp ne <4 x i32> %[[TMP_B]], zeroinitializer
1245+
// LLVM: %[[NE_B_ZERO_SEXT:.*]] = sext <4 x i1> %[[NE_B_ZERO]] to <4 x i32>
1246+
// LLVM: %[[RESULT:.*]] = and <4 x i32> %[[NE_A_ZERO_SEXT]], %[[NE_B_ZERO_SEXT]]
1247+
// LLVM: store <4 x i32> %[[RESULT]], ptr %[[C_ADDR]], align 16
1248+
1249+
// OGCG: %[[A_ADDR:.*]] = alloca <4 x i32>, align 16
1250+
// OGCG: %[[B_ADDR:.*]] = alloca <4 x i32>, align 16
1251+
// OGCG: %[[C_ADDR:.*]] = alloca <4 x i32>, align 16
1252+
// OGCG: %[[TMP_A:.*]] = load <4 x i32>, ptr %[[A_ADDR]], align 16
1253+
// OGCG: %[[TMP_B:.*]] = load <4 x i32>, ptr %[[B_ADDR]], align 16
1254+
// OGCG: %[[NE_A_ZERO:.*]] = icmp ne <4 x i32> %[[TMP_A]], zeroinitializer
1255+
// OGCG: %[[NE_B_ZERO:.*]] = icmp ne <4 x i32> %[[TMP_B]], zeroinitializer
1256+
// OGCG: %[[VEC_OR:.*]] = and <4 x i1> %[[NE_A_ZERO]], %[[NE_B_ZERO]]
1257+
// OGCG: %[[RESULT:.*]] = sext <4 x i1> %[[VEC_OR]] to <4 x i32>
1258+
// OGCG: store <4 x i32> %[[RESULT]], ptr %[[C_ADDR]], align 16

clang/test/CIR/CodeGen/vector.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,3 +1258,43 @@ void foo25() {
12581258
// OGCG: %[[VEC_OR:.*]] = or <4 x i1> %[[NE_A_ZERO]], %[[NE_B_ZERO]]
12591259
// OGCG: %[[RESULT:.*]] = sext <4 x i1> %[[VEC_OR]] to <4 x i32>
12601260
// OGCG: store <4 x i32> %[[RESULT]], ptr %[[C_ADDR]], align 16
1261+
1262+
void foo26() {
1263+
vi4 a;
1264+
vi4 b;
1265+
vi4 c = a && b;
1266+
}
1267+
1268+
// CIR: %[[A_ADDR:.*]] = cir.alloca !cir.vector<4 x !s32i>, !cir.ptr<!cir.vector<4 x !s32i>>, ["a"]
1269+
// CIR: %[[B_ADDR:.*]] = cir.alloca !cir.vector<4 x !s32i>, !cir.ptr<!cir.vector<4 x !s32i>>, ["b"]
1270+
// CIR: %[[C_ADDR:.*]] = cir.alloca !cir.vector<4 x !s32i>, !cir.ptr<!cir.vector<4 x !s32i>>, ["c", init]
1271+
// CIR: %[[ZERO_VEC:.*]] = cir.const #cir.const_vector<[#cir.int<0> : !s32i, #cir.int<0> : !s32i, #cir.int<0> : !s32i, #cir.int<0> : !s32i]> : !cir.vector<4 x !s32i>
1272+
// CIR: %[[TMP_A:.*]] = cir.load{{.*}} %[[A_ADDR]] : !cir.ptr<!cir.vector<4 x !s32i>>, !cir.vector<4 x !s32i>
1273+
// CIR: %[[TMP_B:.*]] = cir.load{{.*}} %[[B_ADDR]] : !cir.ptr<!cir.vector<4 x !s32i>>, !cir.vector<4 x !s32i>
1274+
// CIR: %[[NE_A_ZERO:.*]] = cir.vec.cmp(ne, %[[TMP_A]], %[[ZERO_VEC]]) : !cir.vector<4 x !s32i>, !cir.vector<4 x !s32i>
1275+
// CIR: %[[NE_B_ZERO:.*]] = cir.vec.cmp(ne, %[[TMP_B]], %[[ZERO_VEC]]) : !cir.vector<4 x !s32i>, !cir.vector<4 x !s32i>
1276+
// CIR: %[[RESULT:.*]] = cir.binop(and, %[[NE_A_ZERO]], %[[NE_B_ZERO]]) : !cir.vector<4 x !s32i>
1277+
// CIR: cir.store{{.*}} %[[RESULT]], %[[C_ADDR]] : !cir.vector<4 x !s32i>, !cir.ptr<!cir.vector<4 x !s32i>>
1278+
1279+
// LLVM: %[[A_ADDR:.*]] = alloca <4 x i32>, i64 1, align 16
1280+
// LLVM: %[[B_ADDR:.*]] = alloca <4 x i32>, i64 1, align 16
1281+
// LLVM: %[[C_ADDR:.*]] = alloca <4 x i32>, i64 1, align 16
1282+
// LLVM: %[[TMP_A:.*]] = load <4 x i32>, ptr %[[A_ADDR]], align 16
1283+
// LLVM: %[[TMP_B:.*]] = load <4 x i32>, ptr %[[B_ADDR]], align 16
1284+
// LLVM: %[[NE_A_ZERO:.*]] = icmp ne <4 x i32> %[[TMP_A]], zeroinitializer
1285+
// LLVM: %[[NE_A_ZERO_SEXT:.*]] = sext <4 x i1> %[[NE_A_ZERO]] to <4 x i32>
1286+
// LLVM: %[[NE_B_ZERO:.*]] = icmp ne <4 x i32> %[[TMP_B]], zeroinitializer
1287+
// LLVM: %[[NE_B_ZERO_SEXT:.*]] = sext <4 x i1> %[[NE_B_ZERO]] to <4 x i32>
1288+
// LLVM: %[[RESULT:.*]] = and <4 x i32> %[[NE_A_ZERO_SEXT]], %[[NE_B_ZERO_SEXT]]
1289+
// LLVM: store <4 x i32> %[[RESULT]], ptr %[[C_ADDR]], align 16
1290+
1291+
// OGCG: %[[A_ADDR:.*]] = alloca <4 x i32>, align 16
1292+
// OGCG: %[[B_ADDR:.*]] = alloca <4 x i32>, align 16
1293+
// OGCG: %[[C_ADDR:.*]] = alloca <4 x i32>, align 16
1294+
// OGCG: %[[TMP_A:.*]] = load <4 x i32>, ptr %[[A_ADDR]], align 16
1295+
// OGCG: %[[TMP_B:.*]] = load <4 x i32>, ptr %[[B_ADDR]], align 16
1296+
// OGCG: %[[NE_A_ZERO:.*]] = icmp ne <4 x i32> %[[TMP_A]], zeroinitializer
1297+
// OGCG: %[[NE_B_ZERO:.*]] = icmp ne <4 x i32> %[[TMP_B]], zeroinitializer
1298+
// OGCG: %[[VEC_OR:.*]] = and <4 x i1> %[[NE_A_ZERO]], %[[NE_B_ZERO]]
1299+
// OGCG: %[[RESULT:.*]] = sext <4 x i1> %[[VEC_OR]] to <4 x i32>
1300+
// OGCG: store <4 x i32> %[[RESULT]], ptr %[[C_ADDR]], align 16

clang/test/Driver/uefi-constructed-args.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
// CHECK-SAME: "-mrelocation-model" "pic" "-pic-level" "2"
88
// CHECK-SAME: "-mframe-pointer=all"
99
// CHECK-SAME: "-fms-extensions"
10-
// CHECK-NEXT: "-nologo"
11-
// CHECK-SAME: "-subsystem:efi_application"
12-
// CHECK-SAME: "-entry:EfiMain"
13-
// CHECK-SAME: "-tsaware:no"
14-
// CHECK-SAME: "-debug"
10+
// CHECK-NEXT: "/nologo"
11+
// CHECK-SAME: "/subsystem:efi_application"
12+
// CHECK-SAME: "/entry:EfiMain"
13+
// CHECK-SAME: "/tsaware:no"
14+
// CHECK-SAME: "/debug"

clang/test/SemaCXX/builtin-structured-binding-size.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify
2-
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify -fexperimental-new-constant-interpreter
1+
// RUN: %clang_cc1 %s -triple=x86_64 -std=c++2c -fsyntax-only -verify
2+
// RUN: %clang_cc1 %s -triple=x86_64 -std=c++2c -fsyntax-only -verify -fexperimental-new-constant-interpreter
33

44

55
struct S0 {};
@@ -229,3 +229,19 @@ static_assert(__is_same_as(tag_of_t<S1>, int));
229229
static_assert(__is_same_as(tag_of_t<int>, int)); // error
230230
// expected-error@-1 {{constraints not satisfied for alias template 'tag_of_t' [with T = int]}}
231231
// expected-note@#tag-of-constr {{because substituted constraint expression is ill-formed: type 'int' cannot be decomposed}}
232+
233+
struct MinusOne;
234+
template <> struct ::std::tuple_size<MinusOne> {
235+
static constexpr int value = -1;
236+
};
237+
int minus_one = __builtin_structured_binding_size(MinusOne);
238+
// expected-error@-1 {{cannot decompose this type; 'std::tuple_size<MinusOne>::value' is not a valid size: -1}}
239+
// expected-error@-2 {{type 'MinusOne' cannot be decomposed}}
240+
241+
struct UintMax;
242+
template <> struct ::std::tuple_size<UintMax> {
243+
static constexpr unsigned value = -1;
244+
};
245+
int uint_max = __builtin_structured_binding_size(UintMax);
246+
// expected-error@-1 {{cannot decompose this type; 'std::tuple_size<UintMax>::value' is not a valid size: 4294967295}}
247+
// expected-error@-2 {{type 'UintMax' cannot be decomposed}}

0 commit comments

Comments
 (0)