Skip to content

Commit dfa877e

Browse files
committed
[CIR] Implement cast expression classes as l-values
This commit fixes l-value emission for CXXConstCastExpr, CXXReinterpretCastExpr, CXXFunctionalCastExpr, CXXAddrspaceCastExpr, and ObjCBridgedCastExpr by routing them through the existing emitCastLValue() function, matching the behavior of CodeGen. Previously, these cast expression classes would trigger an error and assertion when used in l-value contexts (e.g., `const_cast<int&>(x) = 1`). This was an artificial limitation - CodeGen routes all cast expression classes through EmitCastLValue(), which then handles each cast *kind* appropriately. Key insight from CodeGen (CGExpr.cpp:5679-5685): "Casts are never lvalues unless that cast is to a reference type." The cast expression *class* (CXXConstCastExpr vs CXXReinterpretCastExpr) doesn't determine if it's a valid l-value. Instead, the cast *kind* (CK_NoOp, CK_LValueBitCast, etc.) and result type determine validity. emitCastLValue() already handles this correctly based on getCastKind(). Implementation Details: - Removed emitError() and assert() for const_cast and related expr classes - Unified all cast expression classes in emitLValue() switch statement - All now route through emitCastLValue() like CodeGen (CGExpr.cpp:1823-1832) - Safety is maintained by emitCastLValue()'s cast kind handling This follows the ClangIR principle of copying implementations from CodeGen, which has been proven correct over 20 years. Testing: - Single comprehensive test covering const_cast and reinterpret_cast - Tests verify CIR emission, LLVM lowering, and match with original CodeGen - All three outputs verified to ensure correctness Test Plan: $ ninja -C build/Release clang $ build/Release/bin/llvm-lit clang/test/CIR/CodeGen/cast-lvalue.cpp ghstack-source-id: f4cfd63 Pull-Request: #2011
1 parent 1cc78f0 commit dfa877e

File tree

4 files changed

+51
-18
lines changed

4 files changed

+51
-18
lines changed

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2710,18 +2710,15 @@ LValue CIRGenFunction::emitLValue(const Expr *E) {
27102710
return emitCompoundLiteralLValue(cast<CompoundLiteralExpr>(E));
27112711
case Expr::PredefinedExprClass:
27122712
return emitPredefinedLValue(cast<PredefinedExpr>(E));
2713+
case Expr::ImplicitCastExprClass:
2714+
case Expr::CStyleCastExprClass:
27132715
case Expr::CXXFunctionalCastExprClass:
2716+
case Expr::CXXStaticCastExprClass:
2717+
case Expr::CXXDynamicCastExprClass:
27142718
case Expr::CXXReinterpretCastExprClass:
27152719
case Expr::CXXConstCastExprClass:
27162720
case Expr::CXXAddrspaceCastExprClass:
27172721
case Expr::ObjCBridgedCastExprClass:
2718-
emitError(getLoc(E->getExprLoc()), "l-value not implemented for '")
2719-
<< E->getStmtClassName() << "'";
2720-
assert(0 && "Use emitCastLValue below, remove me when adding testcase");
2721-
case Expr::CStyleCastExprClass:
2722-
case Expr::CXXStaticCastExprClass:
2723-
case Expr::CXXDynamicCastExprClass:
2724-
case Expr::ImplicitCastExprClass:
27252722
return emitCastLValue(cast<CastExpr>(E));
27262723
case Expr::OpaqueValueExprClass:
27272724
return emitOpaqueValueLValue(cast<OpaqueValueExpr>(E));
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
3+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
4+
// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
5+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.orig.ll
6+
// RUN: FileCheck --check-prefix=LLVM --input-file=%t.orig.ll %s
7+
//
8+
// Test for cast expressions as l-values (const_cast, reinterpret_cast, etc.)
9+
10+
void const_cast_lvalue() {
11+
const int x = 0;
12+
const_cast<int&>(x) = 1;
13+
}
14+
15+
// CIR-LABEL: cir.func dso_local @_Z17const_cast_lvaluev
16+
// CIR: %[[X:.*]] = cir.alloca !s32i, {{.*}}, ["x", init, const]
17+
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
18+
// CIR: cir.store {{.*}} %[[ZERO]], %[[X]] : !s32i, !cir.ptr<!s32i>
19+
// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
20+
// CIR: cir.store {{.*}} %[[ONE]], %[[X]] : !s32i, !cir.ptr<!s32i>
21+
// CIR: cir.return
22+
23+
// LLVM-LABEL: define {{.*}}void @_Z17const_cast_lvaluev
24+
// LLVM: %[[X:.*]] = alloca i32
25+
// LLVM: store i32 0, ptr %[[X]]
26+
// LLVM: store i32 1, ptr %[[X]]
27+
// LLVM: ret void
28+
29+
void reinterpret_cast_lvalue() {
30+
long x = 0;
31+
reinterpret_cast<int&>(x) = 1;
32+
}
33+
34+
// CIR-LABEL: cir.func dso_local @_Z23reinterpret_cast_lvaluev
35+
// CIR: %[[X:.*]] = cir.alloca !s64i, {{.*}}, ["x", init]
36+
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
37+
// CIR: cir.store {{.*}} %{{.*}}, %[[X]] : !s64i, !cir.ptr<!s64i>
38+
// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
39+
// CIR: %[[CAST:.*]] = cir.cast bitcast %[[X]] : !cir.ptr<!s64i> -> !cir.ptr<!s32i>
40+
// CIR: cir.store {{.*}} %[[ONE]], %[[CAST]] : !s32i, !cir.ptr<!s32i>
41+
// CIR: cir.return
42+
43+
// LLVM-LABEL: define {{.*}}void @_Z23reinterpret_cast_lvaluev
44+
// LLVM: %[[X:.*]] = alloca i64
45+
// LLVM: store i64 0, ptr %[[X]]
46+
// LLVM: store i32 1, ptr %[[X]]
47+
// LLVM: ret void

clang/test/CIR/crashes/const-cast-expr-lvalue.cpp

Lines changed: 0 additions & 10 deletions
This file was deleted.

clang/test/CIR/crashes/const-cast-lvalue.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2-
// XFAIL: *
32
//
43
// CXXConstCastExpr l-value emission not implemented
54
// Location: CIRGenExpr.cpp:2799

0 commit comments

Comments
 (0)