-
Notifications
You must be signed in to change notification settings - Fork 15.2k
Fix __builtin_object_size calculation for references of unknown origin in C++23 #157778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
in C++23 This addresses an issue introduced by 0a9c08c, which implemented P2280R4. This fixes the issue reported in llvm#95474 (comment). rdar://149897839
@llvm/pr-subscribers-clang Author: Akira Hatanaka (ahatanak) ChangesThis addresses an issue introduced by 0a9c08c, which implemented P2280R4. This fixes the issue reported in rdar://149897839 Full diff: https://github.com/llvm/llvm-project/pull/157778.diff 2 Files Affected:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 2376e482a19f5..229211c4f2cb7 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -13279,6 +13279,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();
@@ -13328,7 +13331,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);
@@ -13396,6 +13399,17 @@ static bool determineEndOffset(EvalInfo &Info, SourceLocation ExprLoc,
if (LVal.InvalidBase)
return false;
+ // We cannot deterimine the end offset of the enitre object if this is an
+ // unknown reference.
+ if (Type == 0 && LVal.AllowConstexprUnknown)
+ return false;
+
+ // We cannot deterimine the end offset of the subobject if this is an
+ // unknown reference and the subobject designator is invalid (e.g., unsized
+ // array designator).
+ if (Type == 1 && LVal.Designator.Invalid && LVal.AllowConstexprUnknown)
+ return false;
+
QualType BaseTy = getObjectType(LVal.getLValueBase());
const bool Ret = CheckedHandleSizeof(BaseTy, EndOffset);
addFlexibleArrayMemberInitSize(Info, BaseTy, LVal, EndOffset);
diff --git a/clang/test/Sema/builtin-object-size.cpp b/clang/test/Sema/builtin-object-size.cpp
new file mode 100644
index 0000000000000..3995e1880ec81
--- /dev/null
+++ b/clang/test/Sema/builtin-object-size.cpp
@@ -0,0 +1,48 @@
+// 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];
+};
+
+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) == 4);
+static_assert(__builtin_object_size(s2.a, 3) == 2);
+
+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) == 4);
+static_assert(__builtin_object_size(s1.a, 3) == 1);
+
+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) == 0);
+static_assert(__builtin_object_size(s0.a, 3) == 0);
+
+extern EmptyS ∅
+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) == 0);
+static_assert(__builtin_object_size(empty.a, 3)); // expected-error {{static assertion expression is not an integral constant expression}}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect we actually need to bail out in more cases: if the underlying object could become known later, we need to refuse to evaluate __builtin_object_size. If you have extern S<2> &s2;
, the definition could be completed later, and we need to ensure that we don't produce different values based on that.
I'd also like to see some tests for potential constant expression checking (see #151053).
Something like this?
Could you elaborate on what kind of tests you have in mind? |
Try the following with -std=c++23 -Winvalid-constexpr:
|
Does this mean that
Shouldn't it return false when |
In any context where the standard requires constant evaluation, we have to return false, probably. (This corresponds, roughly, to Info.InConstantContext. I don't think we have a bit that precisely corresponds, though.) We probably do want to fold in tryEvaluateObjectSize, though. |
Shouldn't it return false even in contexts that don't require constant evaluation? The following function (adapted from
This is exactly the case where the underlying object could become known later. |
We can't represent Type==3 in LLVM IR; CodeGenFunction::emitBuiltinObjectSize unconditionally bails. So we either evaluate in the frontend, or not at all. |
I see, thank you for clarifying. In that case, we shouldn't return false unless constant evaluation is required. |
- Bail out in more cases. - Test potential constant expression.
For Type=3, conditionally returning false when |
This addresses an issue introduced by 0a9c08c, which implemented P2280R4.
This fixes the issue reported in
#95474 (comment).
rdar://149897839