commit d4a149dfb83a90d38811ac0c1cc8345b82bd9b5a
parent 3b8120ff0d624c22423a5cff80dadaf60abb599c
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Fri, 1 May 2026 17:45:38 -0700
cc/cg: promote narrow integers to (signed) int
C 6.3.1.1 says _Bool, char, short, and any narrower integer type
whose values all fit in int promote to int (signed). cg-promote
previously kept the source signedness — narrow unsigned types went
to u32 — which dragged the subsequent cg-arith-conv into picking
the unsigned common type and flipped the signedness of `>>`,
ordered comparisons, division, etc. away from the C rule. Always
promote to i32; any narrow value's canonical 64-bit slot form
already matches i32 so the cast stays relabel-only.
New cc-cg fixture 84-bool-promotes-int locks in (_Bool=1) * -1
== -1 — previously 0 because the bool→u32 promotion forced an
unsigned multiply.
Diffstat:
3 files changed, 38 insertions(+), 5 deletions(-)
diff --git a/cc/cc.scm b/cc/cc.scm
@@ -3478,12 +3478,16 @@
(ty (opnd-type p))
(sz (%ctype-size ty)))
(cond
+ ;; C 6.3.1.1: _Bool, char, short, and any narrower int type
+ ;; promote to (signed) int — every representable value fits
+ ;; in i32. Treating narrow unsigned types as u32 here would
+ ;; drag the subsequent arith-conv into picking the unsigned
+ ;; common type, flipping signedness of `>>`, comparisons,
+ ;; division, etc. against the C rule. Canonical form for any
+ ;; in-range narrow value already matches i32, so the cast is
+ ;; relabel-only.
((< sz 4)
- (cond
- ((%ctype-unsigned? ty)
- (cg-push cg (%opnd (opnd-kind p) %t-u32 (opnd-ext p) (opnd-lval? p))))
- (else
- (cg-push cg (%opnd (opnd-kind p) %t-i32 (opnd-ext p) (opnd-lval? p))))))
+ (cg-push cg (%opnd (opnd-kind p) %t-i32 (opnd-ext p) (opnd-lval? p))))
(else (cg-push cg p)))))
(define (cg-arith-conv cg)
diff --git a/tests/cc-cg/84-bool-promotes-int.expected-exit b/tests/cc-cg/84-bool-promotes-int.expected-exit
@@ -0,0 +1 @@
+1
diff --git a/tests/cc-cg/84-bool-promotes-int.scm b/tests/cc-cg/84-bool-promotes-int.scm
@@ -0,0 +1,28 @@
+;; tests/cc-cg/84-bool-promotes-int.scm — _Bool promotes to int per
+;; C 6.3.1.1 (integer promotions: _Bool, char, short → int, not unsigned).
+;;
+;; Models:
+;; _Bool b = 1;
+;; return (b * -1) == -1;
+;;
+;; cg-promote currently treats bool as unsigned and promotes to u32,
+;; so the subsequent arith-conv with i32 picks u32 as the common
+;; type. The product 1u * (uint32_t)-1 = 0xFFFFFFFF (canonical
+;; 0x00000000FFFFFFFF after spill), and the equality test against
+;; (int)-1 (canonical 0xFFFFFFFFFFFFFFFF) fails. Correct cg promotes
+;; bool to i32 → arith-conv keeps i32 → product is canonical i32 -1
+;; → equality holds.
+
+(let ((cg (cg-init)))
+ (cg-fn-begin cg "main" '() %t-i32)
+ (cg-push-imm cg %t-bool 1)
+ (cg-promote cg)
+ (cg-push-imm cg %t-i32 -1)
+ (cg-promote cg)
+ (cg-arith-conv cg)
+ (cg-binop cg 'mul)
+ (cg-push-imm cg %t-i32 -1)
+ (cg-binop cg 'eq)
+ (cg-return cg)
+ (cg-fn-end cg)
+ (write-bv-fd 1 (cg-finish cg)))