Skip to content

Commit d4a1754

Browse files
committed
Avoid narrowing conversion errors in initializer lists in C++ -- fixes AeneasVerif/eurydice#138
1 parent e098c05 commit d4a1754

File tree

2 files changed

+61
-45
lines changed

2 files changed

+61
-45
lines changed

lib/AstToCStar.ml

Lines changed: 58 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ let whitelisted_tapp e =
210210
let no_return_type_lids = ref []
211211

212212
let rec unit_to_void env e es extra =
213-
let mk_expr env e = mk_expr env false e in
213+
let mk_expr env e = mk_expr env false false e in
214214
match es with
215215
| [ { node = EUnit; _ } ] ->
216216
CStar.Call (mk_expr env e, [] @ extra)
@@ -313,13 +313,22 @@ and is_arith op w =
313313
(* As an optimization, this function returns a boolean indicating whether the
314314
arithmetic expression was "atomic", i.e. something that certainly doesn't
315315
have extra bits beyond the intended width of the type. Globals, results of
316-
function calls, anything not an arithmetic operation, really, is atomic. *)
316+
function calls, anything not an arithmetic operation, really, is atomic.
317+
318+
We now also return a boolean indicating whether this expression was widened
319+
(per the compilation strategy above).
320+
- In C, we rely on implicit narrowing conversions, and let the (wider)
321+
arithmetic expression be { assigned, returned, passed } at a narrow type.
322+
- In C++, initializer lists are intended to disallow this beahvior, so if the
323+
boolean is true, if we are in C++, and if we are under an initializer list
324+
(mostly, EBufCreateL and/or ECons a.k.a. initializer lists for arrays and
325+
structs), then we narrow-cast, relying on the third boolean. *)
317326
and mk_arith env e =
318327
let mask_if is_atomic w e = if is_atomic then e else mask w e in
319328
match e.node with
320329
| EApp ({ node = EOp (op, w); _ }, [ e1; e2 ]) when is_arith op w ->
321-
let e1, a1 = mk_arith env e1 in
322-
let e2, a2 = mk_arith env e2 in
330+
let e1, a1, w1 = mk_arith env e1 in
331+
let e2, a2, w2 = mk_arith env e2 in
323332
begin match op with
324333
| Add | AddW | Sub | SubW | Mult | MultW | BOr | BAnd | BXor | BNot | BShiftL ->
325334
CStar.Call (Op op, [ e1; e2 ])
@@ -329,27 +338,27 @@ and mk_arith env e =
329338
CStar.Call (Op op, [ mask_if a1 w e1; e2 ])
330339
| _ ->
331340
assert false
332-
end, false
341+
end, false, w1 || w2
333342
| EConstant _ ->
334343
(* Constants are emitted with the U suffix which preserves the invariant
335344
that every subexpression operates over unsigned int until the final
336345
cast, or until a mask is needed to preserve semantics. *)
337-
mk_expr env false e, true
346+
mk_expr env false false e, true, false (* C++: a constant that is wider than the intended type, but in an initializer list, is ok *)
338347
| _ ->
339348
(* Something else. Who knows? Maybe a function call, a field reference, a
340349
variable, a global... which will be upcast into `int`, which is *not*
341350
what we want! (See compilation strategy above.). We cast. *)
342351
if e.typ = TInt UInt16 || e.typ = TInt UInt8 then
343-
CStar.Cast (mk_expr env false e, Int UInt32), true
352+
CStar.Cast (mk_expr env false false e, Int UInt32), true, true
344353
else
345-
mk_expr env false e, true
354+
mk_expr env false false e, true, false
346355

347356
and return_type_not_needed e =
348357
match e.node with
349358
| EQualified lid when List.mem lid !no_return_type_lids -> true
350359
| _ -> false
351360

352-
and mk_expr env in_stmt e =
361+
and mk_expr env in_stmt under_initializer_list e =
353362
(* Wrap in comment node if need be. *)
354363
let meta = match e.node with ELet (b, _, _) -> b.meta @ e.meta | _ -> e.meta in
355364
begin match List.filter_map (function CommentBefore s -> Some s | _ -> None) meta,
@@ -360,7 +369,7 @@ and mk_expr env in_stmt e =
360369
end @@
361370

362371
(* Actual translation. *)
363-
let mk_expr env e = mk_expr env false e in
372+
let mk_expr env u e = mk_expr env false u e in
364373
match e.node with
365374
| EBound var ->
366375
CStar.Var (find env var)
@@ -387,7 +396,11 @@ and mk_expr env in_stmt e =
387396
unit_to_void env e0 (cgs @ cgs') (List.map (fun t -> CStar.Type (mk_type env t)) ts @ [ ret_t ])
388397

389398
| EApp ({ node = EOp (op, w); _ }, [ _; _ ]) when is_arith op w ->
390-
fst (mk_arith env e)
399+
let e', _, widened = mk_arith env e in
400+
if !Options.cxx_compat && under_initializer_list && widened then
401+
Cast (e', mk_type env e.typ)
402+
else
403+
e'
391404

392405
| EApp (e, es) ->
393406
(* Functions that only take a unit take no argument. *)
@@ -404,15 +417,15 @@ and mk_expr env in_stmt e =
404417
else
405418
call
406419
| EBufCreate (l, e1, e2) ->
407-
CStar.BufCreate (l, mk_expr env e1, mk_expr env e2)
420+
CStar.BufCreate (l, mk_expr env false e1, mk_expr env false e2)
408421
| EBufCreateL (l, es) ->
409-
CStar.BufCreateL (l, List.map (mk_expr env) es)
422+
CStar.BufCreateL (l, List.map (mk_expr env true) es)
410423
| EBufRead (e1, e2) ->
411-
CStar.BufRead (mk_expr env e1, mk_expr env e2)
424+
CStar.BufRead (mk_expr env false e1, mk_expr env false e2)
412425
| EBufSub (e1, e2) ->
413-
CStar.BufSub (mk_expr env e1, mk_expr env e2)
426+
CStar.BufSub (mk_expr env false e1, mk_expr env false e2)
414427
| EBufDiff (e1, e2) ->
415-
CStar.Call (CStar.Op K.Sub, [mk_expr env e1; mk_expr env e2])
428+
CStar.Call (CStar.Op K.Sub, [mk_expr env false e1; mk_expr env false e2])
416429
| EOp (o, _) ->
417430
CStar.Op o
418431
| EPolyComp (c, _) ->
@@ -421,7 +434,7 @@ and mk_expr env in_stmt e =
421434
scalar type in C that is supported by C's equality comparison. *)
422435
CStar.Op (K.op_of_poly_comp c)
423436
| ECast (e, t) ->
424-
CStar.Cast (mk_expr env e, mk_type env t)
437+
CStar.Cast (mk_expr env false e, mk_type env t)
425438
| EAbort (t, s) ->
426439
let t = match t with Some t -> t | None -> e.typ in
427440
CStar.EAbort (mk_type env t, Option.value ~default:"" s)
@@ -436,13 +449,13 @@ and mk_expr env in_stmt e =
436449
| EFlat fields ->
437450
let name = match e.typ with TQualified lid -> Some lid | _ -> None in
438451
CStar.Struct (name, List.map (fun (name, expr) ->
439-
name, mk_expr env expr
452+
name, mk_expr env true expr
440453
) fields)
441454
| EField (expr, field) ->
442-
CStar.Field (mk_expr env expr, field)
455+
CStar.Field (mk_expr env false expr, field)
443456

444457
| EAddrOf e ->
445-
CStar.AddrOf (mk_expr env e)
458+
CStar.AddrOf (mk_expr env false e)
446459

447460
| EBufNull ->
448461
CStar.BufNull
@@ -459,9 +472,9 @@ and mk_ignored_stmt env e =
459472
let s =
460473
match e.typ with
461474
| TUnit ->
462-
CStar.Ignore (mk_expr env true e)
475+
CStar.Ignore (mk_expr env true false e)
463476
| _ ->
464-
CStar.Ignore (CStar.Cast (mk_expr env true e, CStar.Void))
477+
CStar.Ignore (CStar.Cast (mk_expr env true false e, CStar.Void))
465478
in
466479
env, [s]
467480

@@ -486,12 +499,12 @@ and mk_stmts env e ret_type =
486499
match e.node with
487500
| ELet (b, e1, e2) ->
488501
let env, binder = mk_and_push_binder env b Local (Some e1) [ e2 ]
489-
and e1 = mk_expr env false e1 in
502+
and e1 = mk_expr env false false e1 in
490503
let acc = CStar.Decl (binder, e1) :: comment (e.meta @ b.meta) @ acc in
491504
collect (env, acc) return_pos e2
492505

493506
| EWhile (e1, e2) ->
494-
let e' = CStar.While (mk_expr env false e1, mk_block env Not e2) in
507+
let e' = CStar.While (mk_expr env false false e1, mk_block env Not e2) in
495508
env, maybe_return (e' :: comment e.meta @ acc)
496509

497510
| EFor (binder,
@@ -545,7 +558,7 @@ and mk_stmts env e ret_type =
545558
let is_solo_assignment = binder.node.meta = Some MetaSequence in
546559
(* TODO: shouldn't e1 be added here? *)
547560
let env', binder = mk_and_push_binder env binder Local (Some e1) [ e2; e3; e4 ] in
548-
let e2 = mk_expr env' false e2 in
561+
let e2 = mk_expr env' false false e2 in
549562
let e3 = KList.last (mk_block env' Not e3) in
550563
let e4 = mk_block env' Not e4 in
551564
let e' =
@@ -557,7 +570,7 @@ and mk_stmts env e ret_type =
557570
in
558571
CStar.For (e1, e2, e3, e4)
559572
else
560-
let e1 = mk_expr env false e1 in
573+
let e1 = mk_expr env false false e1 in
561574
CStar.For (`Decl (binder, e1), e2, e3, e4)
562575
in
563576
env', maybe_return (e' :: comment e.meta @ acc)
@@ -586,7 +599,7 @@ and mk_stmts env e ret_type =
586599
if not !Options.no_return_else || return_pos = Not then
587600
(* No optimization *)
588601
let e' = CStar.IfThenElse (false,
589-
mk_expr env false e1,
602+
mk_expr env false false e1,
590603
mk_block env return_pos e2,
591604
mk_block env return_pos e3
592605
) in
@@ -595,7 +608,7 @@ and mk_stmts env e ret_type =
595608
(* no_return_else && return_pos <> Not,
596609
* i.e. optimization enabled; we are in return position *)
597610
let e' = CStar.IfThenElse (false,
598-
mk_expr env false e1,
611+
mk_expr env false false e1,
599612
mk_block env Must e2,
600613
[]
601614
) in
@@ -613,39 +626,39 @@ and mk_stmts env e ret_type =
613626
assert false
614627

615628
| EAssign (e1, e2) ->
616-
let e' = CStar.Assign (mk_expr env false e1, mk_type env e1.typ, mk_expr env false e2) in
629+
let e' = CStar.Assign (mk_expr env false false e1, mk_type env e1.typ, mk_expr env false false e2) in
617630
env, maybe_return (e' :: comment e.meta @ acc)
618631

619632
| EBufBlit (e1, e2, e3, e4, e5) ->
620633
let e' = CStar.BufBlit (
621634
mk_type env (assert_tbuf_or_tarray e1.typ),
622-
mk_expr env false e1,
623-
mk_expr env false e2,
624-
mk_expr env false e3,
625-
mk_expr env false e4,
626-
mk_expr env false e5
635+
mk_expr env false false e1,
636+
mk_expr env false false e2,
637+
mk_expr env false false e3,
638+
mk_expr env false false e4,
639+
mk_expr env false false e5
627640
) in
628641
env, maybe_return (e' :: comment e.meta @ acc)
629642

630643
| EBufWrite (e1, e2, e3) ->
631644
let e' = CStar.BufWrite (
632-
mk_expr env false e1,
633-
mk_expr env false e2,
634-
mk_expr env false e3
645+
mk_expr env false false e1,
646+
mk_expr env false false e2,
647+
mk_expr env false false e3
635648
) in
636649
env, maybe_return (e' :: comment e.meta @ acc)
637650

638651
| EBufFill (e1, e2, e3) ->
639652
let e' = CStar.BufFill (
640653
mk_type env (assert_tbuf e1.typ),
641-
mk_expr env false e1,
642-
mk_expr env false e2,
643-
mk_expr env false e3
654+
mk_expr env false false e1,
655+
mk_expr env false false e2,
656+
mk_expr env false false e3
644657
) in
645658
env, maybe_return (e' :: comment e.meta @ acc)
646659

647660
| EBufFree e ->
648-
let e' = CStar.BufFree (mk_type env (assert_tbuf e.typ), mk_expr env false e) in
661+
let e' = CStar.BufFree (mk_type env (assert_tbuf e.typ), mk_expr env false false e) in
649662
env, maybe_return (e' :: comment e.meta @ acc)
650663

651664
| EMatch _ ->
@@ -663,7 +676,7 @@ and mk_stmts env e ret_type =
663676
| [] -> None
664677
| _ -> failwith "impossible"
665678
in
666-
env, CStar.Switch (mk_expr env false e,
679+
env, CStar.Switch (mk_expr env false false e,
667680
List.map (fun (lid, e) ->
668681
(match lid with
669682
| SConstant k -> `Int k
@@ -699,7 +712,7 @@ and mk_stmts env e ret_type =
699712
if is_readonly_c_expression e then
700713
env, comment e.meta @ acc
701714
else
702-
let e' = CStar.Ignore (mk_expr env true e) in
715+
let e' = CStar.Ignore (mk_expr env true false e) in
703716
env, e' :: comment e.meta @ acc
704717

705718
and mk_block env return_pos e =
@@ -716,7 +729,7 @@ and mk_stmts env e ret_type =
716729
let env, s = mk_ignored_stmt env e in
717730
env, maybe_return_nothing (s @ acc)
718731
else
719-
env, CStar.Return (Some (mk_expr env false e)) :: acc
732+
env, CStar.Return (Some (mk_expr env false false e)) :: acc
720733

721734
and mk_ifdef env return_pos acc e1 e2 e3 =
722735
try
@@ -924,7 +937,7 @@ and mk_declaration m env d: (CStar.decl * _) option =
924937
macro,
925938
flags,
926939
mk_type env t,
927-
mk_expr env false body), [])
940+
mk_expr env false false body), [])
928941

929942
| DExternal (cc, flags, _, n, name, t, pp) ->
930943
if LidSet.mem name env.ifdefs || n > 0 then

src/Karamel.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,9 @@ Supported options:|}
561561
let files = Builtin.make_libraries files in
562562
let files = if Options.wasm () then SimplifyWasm.intrinsics#visit_files () files else files in
563563
let files = Bundles.topological_sort files in
564+
if Options.debug "topological" then
565+
KPrint.bprintf "file order after first topological sort: %s\n"
566+
(String.concat " " (List.map fst files));
564567

565568
(* 1. We create bundles, and monomorphize functions first. This creates more
566569
* applications of parameterized types, which we make sure are in the AST by

0 commit comments

Comments
 (0)