Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13336,6 +13336,9 @@ static bool refersToCompleteObject(const LValue &LVal) {
if (LVal.Designator.Invalid)
return false;

if (LVal.AllowConstexprUnknown)
return false;

if (!LVal.Designator.Entries.empty())
return LVal.Designator.isMostDerivedAnUnsizedArray();

Expand Down Expand Up @@ -13385,7 +13388,7 @@ static bool isUserWritingOffTheEnd(const ASTContext &Ctx, const LValue &LVal) {
return false;
};

return LVal.InvalidBase &&
return (LVal.InvalidBase || LVal.AllowConstexprUnknown) &&
Designator.Entries.size() == Designator.MostDerivedPathLength &&
Designator.MostDerivedIsArrayElement && isFlexibleArrayMember() &&
isDesignatorAtObjectEnd(Ctx, LVal);
Expand Down Expand Up @@ -13453,6 +13456,19 @@ static bool determineEndOffset(EvalInfo &Info, SourceLocation ExprLoc,
if (LVal.InvalidBase)
return false;

if (LVal.AllowConstexprUnknown) {
// We cannot deterimine the end offset of the enitre object.
if ((Type == 0 || Type == 2))
return false;

// We cannot deterimine the end offset of the subobject if the subobject
// designator is invalid (e.g., unsized array designator).
if (LVal.Designator.Invalid) {
assert(Type != 3 && "cannot be Type 3");
return false;
}
}

QualType BaseTy = getObjectType(LVal.getLValueBase());
const bool Ret = CheckedHandleSizeof(BaseTy, EndOffset);
addFlexibleArrayMemberInitSize(Info, BaseTy, LVal, EndOffset);
Expand All @@ -13478,10 +13494,14 @@ static bool determineEndOffset(EvalInfo &Info, SourceLocation ExprLoc,
return convertUnsignedAPIntToCharUnits(APEndOffset, EndOffset);

// If we cannot determine the size of the initial allocation, then we can't
// given an accurate upper-bound. However, we are still able to give
// conservative lower-bounds for Type=3.
// give an accurate upper-bound.
if (Type == 1)
return false;

// However, we are still able to give conservative lower-bounds if Type=3
// and this isn't an unknown reference.
if (LVal.AllowConstexprUnknown)
return false;
}

CharUnits BytesPerElem;
Expand All @@ -13498,6 +13518,10 @@ static bool determineEndOffset(EvalInfo &Info, SourceLocation ExprLoc,
uint64_t ArrayIndex = Designator.Entries.back().getAsArrayIndex();
ElemsRemaining = ArraySize <= ArrayIndex ? 0 : ArraySize - ArrayIndex;
} else {
// If this is an unknown reference and there are no subobject designators,
// we cannot determine whether the object is an array element.
if (LVal.AllowConstexprUnknown && LVal.Designator.Entries.empty())
return false;
ElemsRemaining = Designator.isOnePastTheEnd() ? 0 : 1;
}

Expand Down Expand Up @@ -13613,7 +13637,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
case EvaluationMode::ConstantFold:
case EvaluationMode::IgnoreSideEffects:
// Leave it to IR generation.
return Error(E);
return Info.CheckingPotentialConstantExpression ? false : Error(E);
case EvaluationMode::ConstantExpressionUnevaluated:
// Reduce it to a constant now.
return Success((Type & 2) ? 0 : -1, E);
Expand Down
30 changes: 30 additions & 0 deletions clang/test/CodeGenCXX/builtin-object-size.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin -fstrict-flex-arrays=0 -std=c++23 -emit-llvm -o - %s | FileCheck %s

struct EmptyS {
int i;
char a[];
};

template <unsigned N>
struct S {
int i;
char a[N];
};

// CHECK-LABEL: define noundef i32 @_Z4testRK6EmptyS(
// CHECK: ret i32 0
unsigned test(const EmptyS &empty) {
return __builtin_object_size(empty.a, 3);
}

// CHECK-LABEL: define noundef i32 @_Z4testRK1SILj2EE(
// CHECK: ret i32 0
unsigned test(const S<2> &s2) {
return __builtin_object_size(s2.a, 3);
}

// CHECK-LABEL: define noundef i32 @_Z4testRi(
// CHECK: ret i32 0
unsigned test(int &i) {
return __builtin_object_size(&i, 3);
}
73 changes: 73 additions & 0 deletions clang/test/SemaCXX/builtin-object-size.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin -fsyntax-only -fstrict-flex-arrays=0 -DSTRICT0 -std=c++23 -verify %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin -fsyntax-only -fstrict-flex-arrays=1 -DSTRICT1 -std=c++23 -verify %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin -fsyntax-only -fstrict-flex-arrays=2 -DSTRICT2 -std=c++23 -verify %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin -fsyntax-only -fstrict-flex-arrays=3 -DSTRICT3 -std=c++23 -verify %s

struct EmptyS {
int i;
char a[];
};

template <unsigned N>
struct S {
int i;
char a[N];
};

struct T {
int a, b, c;
};

extern S<2> &s2;
static_assert(__builtin_object_size(s2.a, 0)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(s2.a, 1) == 2);
#if defined(STRICT0)
// expected-error@-2 {{static assertion expression is not an integral constant expression}}
#endif
static_assert(__builtin_object_size(s2.a, 2)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(s2.a, 3) == 2);
#if defined(STRICT0)
// expected-error@-2 {{static assertion expression is not an integral constant expression}}
#endif

extern S<1> &s1;
static_assert(__builtin_object_size(s1.a, 0)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(s1.a, 1) == 1);
#if defined(STRICT0) || defined(STRICT1)
// expected-error@-2 {{static assertion expression is not an integral constant expression}}
#endif
static_assert(__builtin_object_size(s1.a, 2)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(s1.a, 3) == 1);
#if defined(STRICT0) || defined(STRICT1)
// expected-error@-2 {{static assertion expression is not an integral constant expression}}
#endif

extern S<0> &s0;
static_assert(__builtin_object_size(s0.a, 0)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(s0.a, 1) == 0);
#if defined(STRICT0) || defined(STRICT1) || defined(STRICT2)
// expected-error@-2 {{static assertion expression is not an integral constant expression}}
#endif
static_assert(__builtin_object_size(s0.a, 2)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(s0.a, 3) == 0);
#if defined(STRICT0) || defined(STRICT1) || defined(STRICT2)
// expected-error@-2 {{static assertion expression is not an integral constant expression}}
#endif

extern EmptyS &empty;
static_assert(__builtin_object_size(empty.a, 0)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(empty.a, 1)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(empty.a, 2)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(empty.a, 3)); // expected-error {{static assertion expression is not an integral constant expression}}

extern T &t;
static_assert(__builtin_object_size(&t.b, 0)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(&t.b, 1) == 4);
static_assert(__builtin_object_size(&t.b, 2)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(&t.b, 3) == 4);

extern int &i;
static_assert(__builtin_object_size(&i, 0)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(&i, 1)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(&i, 2)); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(__builtin_object_size(&i, 3)); // expected-error {{static assertion expression is not an integral constant expression}}
15 changes: 15 additions & 0 deletions clang/test/SemaCXX/constant-expression-p2280r4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,3 +431,18 @@ namespace InvalidConstexprFn {
static_assert(sub(arr, arr) == 0);
static_assert(add(arr[0]) == &arr[3]);
}

namespace BuiltinObjectSize {
constexpr int f0(int &a) {
return 1 / (__builtin_object_size(&a, 0) - 4);
}
constexpr int f1(int &a) {
return 1 / (__builtin_object_size(&a, 1) - 4);
}
constexpr int f2(int &a) {
return 1 / (__builtin_object_size(&a, 2) - 4);
}
constexpr int f3(int &a) {
return 1 / (__builtin_object_size(&a, 3) - 4);
}
}