This is an archive of the discontinued LLVM Phabricator instance.

[clang] Don't short-circuit constant evaluation for array or record types
ClosedPublic

Authored by cmagahern on Jan 13 2023, 6:27 PM.

Details

Summary

FastEvaluateAsRValue returns true without setting a result value for when a
given constant expression is an array or record type.

Clang attributes must be able to support constant expressions that are array or
record types, so proceed with the slower path for evaluation in the case where
FastEvaluateAsRValue does not yield an evaluation result.

Diff Detail

Event Timeline

cmagahern created this revision.Jan 13 2023, 6:27 PM
Herald added a project: Restricted Project. · View Herald TranscriptJan 13 2023, 6:27 PM
cmagahern requested review of this revision.Jan 13 2023, 6:27 PM

Is there no way to add a test for this?

First, y es, we need some sort of tests for this. Second, I've got no idea what you're trying to accomplish here? If you want the array to be evaluatable as a constant-expression, I'd presume the 'Kind' there would be involved somehow, instead of just whether the evaluation was const.

First, y es, we need some sort of tests for this.

We have a test for this in Apple's internal branch of llvm. One of the tests verifies the codegen behavior for a clang attribute that expects an inline struct as an argument to the attribute.

I agree that we should have a test for this independent of the clang attribute, which is dependent on yet not entirely related to this part of the codebase.

Second, I've got no idea what you're trying to accomplish here? If you want the array to be evaluatable as a constant-expression, I'd presume the 'Kind' there would be involved somehow, instead of just whether the evaluation was const.

The goal of the attribute is to allow constant expressions to be written as arguments to that attribute, which get parsed and generated as metadata and associated with the attributed decl. The details of this attribute are arguably not important—Expr::EvaluateAsConstantExpr no longer works as promised because it bails out if the constant expression is not representable as an r-value. This has worked in every version of clang until ec782951d7bd3 (https://reviews.llvm.org/D138115) which is the change that introduced this fast path and broke constant evaluation for array and record types.

Is there no way to add a test for this?

Can you advise on how a test for this can be written? The original change that broke this behavior (https://reviews.llvm.org/D138115) did not add a test either.

(I don't have a good idea of how to test this, I'm still not clear on what was 'broken'. We allowed the original patch, since it was an NFC (or at least intended) so not breaking all the existing test suites was sufficient, whereas this has an intended purpose.

(I don't have a good idea of how to test this, I'm still not clear on what was 'broken'. ...)

EvaluateAsConstantExpr was broken for any expression that is either an array or record type.

For example, the following code that uses the annotate attribute, used by static analyzers, triggers an assertion in clang without this change.

struct my_struct {
	int a;
	const char *b;
};

int x __attribute((annotate(
	"my_annotation", 
	(struct my_struct) { .a = 42, .b = "test" }
)));

The expectation is that this attribute emits constant struct declarations instead:

@x = common global i32 0, align 4
@.str = private unnamed_addr constant [14 x i8] c"my_annotation\00", section "llvm.metadata"
@.str.1 = private unnamed_addr constant [10 x i8] c"./annot.c\00", section "llvm.metadata"
@.str.2 = private unnamed_addr constant [5 x i8] c"test\00", align 1
@.args = private unnamed_addr constant { %struct.my_struct } { %struct.my_struct { i32 42, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.2, i32 0, i32 0) } }, section "llvm.metadata"

(I don't have a good idea of how to test this, I'm still not clear on what was 'broken'. ...)

EvaluateAsConstantExpr was broken for any expression that is either an array or record type.

For example, the following code that uses the annotate attribute, used by static analyzers, triggers an assertion in clang without this change.

struct my_struct {
	int a;
	const char *b;
};

int x __attribute((annotate(
	"my_annotation", 
	(struct my_struct) { .a = 42, .b = "test" }
)));

The expectation is that this attribute emits constant struct declarations instead:

@x = common global i32 0, align 4
@.str = private unnamed_addr constant [14 x i8] c"my_annotation\00", section "llvm.metadata"
@.str.1 = private unnamed_addr constant [10 x i8] c"./annot.c\00", section "llvm.metadata"
@.str.2 = private unnamed_addr constant [5 x i8] c"test\00", align 1
@.args = private unnamed_addr constant { %struct.my_struct } { %struct.my_struct { i32 42, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.2, i32 0, i32 0) } }, section "llvm.metadata"

That seems like a somewhat reasonable test to me.

That seems like a somewhat reasonable test to me.

Sounds good—I'll add something like this to the existing attr-annotate.cpp test.

Updating D141745: [clang] Don't short-circuit constant evaluation for array or record types

Added a test to 2007-06-15-AnnotateAttribute.c since the code has to use a language other than C++11.

erichkeane added inline comments.Jan 17 2023, 1:02 PM
clang/lib/AST/ExprConstant.cpp
15316

Interestingly, the 'IsConst' is used 1x as the return value from FastEvaluateAsRValue, so I wonder if this originally should have been "return IsConst". @tbaeder : got any comments here?

That said, I'm tempted to approve once others have a look, the 'return value' is true if the function has successfully set IsConst, false otherwise. So it is a touch shocking that we wouldn't need to test it.

cmagahern added inline comments.Jan 17 2023, 2:12 PM
clang/lib/AST/ExprConstant.cpp
15316

There are definitely some naming improvements to make here. I'm sort of abusing IsConst since it is exposing exactly the right state that I need, but it kind of seems like FastEvaluateAsRValue should return false if it didn't write anything to Result.

tbaeder added inline comments.Jan 17 2023, 11:30 PM
clang/lib/AST/ExprConstant.cpp
15316

FastEvaluateAsRValue returns "whether it even looked at the given expression" (via the return value) and "whether it checked the expression and decided it's constant" (via IsConst).

Technically speaking, the patch as it is will also try to evaluate expressions with a null type (the third case in FastEvaluateAsRValue), but I don't know if that is relevant at all.

It might make sense to use Result.Val.hasValue() instead of IsConst?

cmagahern added inline comments.Jan 18 2023, 3:52 PM
clang/lib/AST/ExprConstant.cpp
15316

That sounds better, thanks. I'll make the change.

cmagahern updated this revision to Diff 490326.Jan 18 2023, 3:56 PM

Updating D141745: [clang] Don't short-circuit constant evaluation for array or record types

Use Result.Val.hasValue() instead of IsConst to see if
FastEvaluateAsRValue wrote a value into the result variable.

cmagahern marked 2 inline comments as done.Jan 18 2023, 3:56 PM

LGTM but I'll wait for one of the others to comment.

erichkeane accepted this revision.Jan 19 2023, 6:01 AM
This revision is now accepted and ready to land.Jan 19 2023, 6:01 AM
This revision was landed with ongoing or failed builds.Jan 19 2023, 6:04 PM
This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptJan 19 2023, 6:04 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript