commit 632c4d8ec06b73f40521e0d8eca6db45250bee49
parent 75c2bc723023f8c895930d2c4855045d9eb8ed41
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sun, 26 Apr 2026 22:39:46 -0700
cc/parse: per-arg cast-or-promote in parse-call-args (§G.1, §K.5)
parse-call-args now inspects the callee's fn type:
- fixed-arg index: emits cg-cast to the declared param type, so an
out-of-range int gets narrowed before the call (§K.5).
- variadic-arg index: emits cg-promote (§G.1) for default integer
promotion of rank-below-int args.
call-fn-type peeks the vstack top to recover the callee's ctype,
walking through one ptr-to-fn level for function-pointer calls.
Diffstat:
5 files changed, 84 insertions(+), 7 deletions(-)
diff --git a/cc/parse.scm b/cc/parse.scm
@@ -906,7 +906,8 @@
(cg-push-deref (ps-cg ps)) (lp))
((eq? v 'lparen)
(advance ps) (rval-not-fn! ps)
- (let ((n (parse-call-args ps)))
+ (let* ((fn-ty (call-fn-type (ps-cg ps)))
+ (n (parse-call-args ps fn-ty)))
(expect-punct ps 'rparen)
(cg-call (ps-cg ps) n #t)
(lp)))
@@ -936,15 +937,61 @@
(cg-assign (ps-cg ps)) (lp))
(else #t))))))))
-(define (parse-call-args ps)
+;; call-fn-type cg -> ctype-or-#f
+;; The function operand sits at the top of the vstack when
+;; parse-call-args runs (just after rval-not-fn!). Its type may be
+;; `fn` directly (named callee) or `ptr -> fn` (function pointer).
+;; Returns the underlying `fn` ctype, or #f if the operand isn't
+;; recognizably callable (callsite still works — no per-arg cast).
+(define (call-fn-type cg)
+ (let* ((tp (cg-top cg)))
+ (cond
+ ((not tp) #f)
+ (else
+ (let* ((ty (opnd-type tp))
+ (k (ctype-kind ty)))
+ (cond
+ ((eq? k 'fn) ty)
+ ((eq? k 'ptr)
+ (let ((pe (ctype-ext ty)))
+ (cond ((and pe (eq? (ctype-kind pe) 'fn)) pe)
+ (else #f))))
+ (else #f)))))))
+
+;; param-types-of fn-ty -> (params variadic?) with a #f fallback.
+(define (call-fn-param-info fn-ty)
+ (cond
+ ((not fn-ty) (cons '() #f))
+ (else
+ (let ((ext (ctype-ext fn-ty)))
+ (cons (cadr ext) (car (cddr ext)))))))
+
+;; parse-call-args ps fn-ty -> arg-count
+;; Casts each fixed arg to the declared param type (CC.md §K.5).
+;; For variadic args (index >= named-arg count, when variadic? = #t)
+;; applies cg-promote (CC.md §G.1).
+(define (parse-call-args ps fn-ty)
(cond
((at-punct? ps 'rparen) 0)
(else
- (let lp ((n 0))
- (parse-expr-bp ps 4) (rval! ps)
- (let ((m (+ n 1)))
- (cond ((at-punct? ps 'comma) (advance ps) (lp m))
- (else m)))))))
+ (let* ((info (call-fn-param-info fn-ty))
+ (params (car info))
+ (var? (cdr info))
+ (nfix (length params)))
+ (let lp ((n 0) (rem params))
+ (parse-expr-bp ps 4) (rval! ps)
+ (cond
+ ;; Fixed-arg: cast to declared param type. param entry shape
+ ;; is (name . ctype) per cg-fn-begin's contract.
+ ((not (null? rem))
+ (cg-cast (ps-cg ps) (cdr (car rem))))
+ ;; Variadic position (n >= nfix and var? is true): promote.
+ (var?
+ (cg-promote (ps-cg ps))))
+ (let ((m (+ n 1))
+ (rest (if (null? rem) '() (cdr rem))))
+ (cond ((at-punct? ps 'comma) (advance ps) (lp m rest))
+ (else m))))))))
(define (parse-primary ps)
(let ((t (peek ps)))
diff --git a/tests/cc-parse/67-vararg-call.c b/tests/cc-parse/67-vararg-call.c
@@ -0,0 +1,22 @@
+/* §G.1 — variadic call: caller default-promotes args.
+ *
+ * Declares sum3 as (int n, ...). Defines a same-ABI sum3 that takes
+ * three ints. Calls sum3(1, c, s) where c is signed char = -3 and s
+ * is short = 100. Per default-promote rules, c and s are promoted to
+ * int before the call, so the callee's a1/a2 hold -3 and 100 with
+ * full 32-bit sign-extension.
+ *
+ * Result: 1 + (-3) + 100 = 98.
+ */
+
+int sum3(int n, ...);
+
+int main(void) {
+ signed char c; short s;
+ c = -3; s = 100;
+ return sum3(1, c, s); /* 1 + (-3) + 100 = 98 */
+}
+
+int sum3(int n, int a, int b) {
+ return n + a + b;
+}
diff --git a/tests/cc-parse/67-vararg-call.expected-exit b/tests/cc-parse/67-vararg-call.expected-exit
@@ -0,0 +1 @@
+98
diff --git a/tests/cc-parse/74-call-narrow.c b/tests/cc-parse/74-call-narrow.c
@@ -0,0 +1,6 @@
+/* §K.5 — fixed-arg call argument is cast to declared param type.
+ * The callee declares (unsigned char x). Caller passes 258. The cast
+ * happens at the call site: 258 & 0xff = 2, so f returns 2. */
+int f(unsigned char x) { return x; }
+
+int main(void) { return f(258); }
diff --git a/tests/cc-parse/74-call-narrow.expected-exit b/tests/cc-parse/74-call-narrow.expected-exit
@@ -0,0 +1 @@
+2