Skip to content

Commit

Permalink
Fix C++ compatibility of unions with degenerate variant names
Browse files Browse the repository at this point in the history
This fixes the C codegen so that when a union has a variant with the same name as the union, the generated code can be compiled as C or C++.

Prior to this change it was valid C but not valid C++ due to differences in the rules for anonymous structs.

I also added a test that fails before this change and passes afterwards.
  • Loading branch information
Timmmm authored and Alasdair committed Jan 7, 2025
1 parent c2fe0d4 commit 68d3839
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 15 deletions.
27 changes: 15 additions & 12 deletions src/sail_c_backend/c_backend.ml
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,7 @@ let rec sgen_cval = function
| V_struct (fields, _) ->
Printf.sprintf "{%s}"
(Util.string_of_list ", " (fun (field, cval) -> zencode_id field ^ " = " ^ sgen_cval cval) fields)
| V_ctor_unwrap (f, ctor, _) -> Printf.sprintf "%s.%s" (sgen_cval f) (sgen_uid ctor)
| V_ctor_unwrap (f, ctor, _) -> Printf.sprintf "%s.variants.%s" (sgen_cval f) (sgen_uid ctor)
| V_tuple _ -> Reporting.unreachable Parse_ast.Unknown __POS__ "Cannot generate C value for a tuple literal"

and sgen_call op cvals =
Expand Down Expand Up @@ -1475,7 +1475,8 @@ let codegen_type_def =
c_function ~return:"static void" (sail_create n "struct %s *op" n)
([string (Printf.sprintf "op->kind = Kind_%s;" (sgen_id ctor_id))]
@
if not (is_stack_ctyp ctyp) then [sail_create ~suffix:";" (sgen_ctyp_name ctyp) "&op->%s" (sgen_id ctor_id)]
if not (is_stack_ctyp ctyp) then
[sail_create ~suffix:";" (sgen_ctyp_name ctyp) "&op->variants.%s" (sgen_id ctor_id)]
else []
)
in
Expand All @@ -1485,7 +1486,7 @@ let codegen_type_def =
in
let clear_field v ctor_id ctyp =
if is_stack_ctyp ctyp then None
else Some (sail_kill ~suffix:";" (sgen_ctyp_name ctyp) "&%s->%s" v (sgen_id ctor_id))
else Some (sail_kill ~suffix:";" (sgen_ctyp_name ctyp) "&%s->variants.%s" v (sgen_id ctor_id))
in
let codegen_clear =
let n = sgen_id id in
Expand All @@ -1499,23 +1500,24 @@ let codegen_type_def =
)
([each_ctor "rop->" (clear_field "rop") tus; string ("rop->kind = Kind_" ^ sgen_id ctor_id) ^^ semi]
@
if is_stack_ctyp ctyp then [ksprintf string "rop->%s = op;" (sgen_id ctor_id)]
if is_stack_ctyp ctyp then [ksprintf string "rop->variants.%s = op;" (sgen_id ctor_id)]
else
[
sail_create ~suffix:";" (sgen_ctyp_name ctyp) "&rop->%s" (sgen_id ctor_id);
sail_copy ~suffix:";" (sgen_ctyp_name ctyp) "&rop->%s, op" (sgen_id ctor_id);
sail_create ~suffix:";" (sgen_ctyp_name ctyp) "&rop->variants.%s" (sgen_id ctor_id);
sail_copy ~suffix:";" (sgen_ctyp_name ctyp) "&rop->variants.%s, op" (sgen_id ctor_id);
]
)
in
let codegen_setter =
let n = sgen_id id in
let set_field ctor_id ctyp =
Some
( if is_stack_ctyp ctyp then string (Printf.sprintf "rop->%s = op.%s;" (sgen_id ctor_id) (sgen_id ctor_id))
( if is_stack_ctyp ctyp then
string (Printf.sprintf "rop->variants.%s = op.variants.%s;" (sgen_id ctor_id) (sgen_id ctor_id))
else
sail_create ~suffix:";" (sgen_ctyp_name ctyp) "&rop->%s" (sgen_id ctor_id)
^^ sail_copy ~prefix:" " ~suffix:";" (sgen_ctyp_name ctyp) "&rop->%s, op.%s" (sgen_id ctor_id)
(sgen_id ctor_id)
sail_create ~suffix:";" (sgen_ctyp_name ctyp) "&rop->variants.%s" (sgen_id ctor_id)
^^ sail_copy ~prefix:" " ~suffix:";" (sgen_ctyp_name ctyp) "&rop->variants.%s, op.variants.%s"
(sgen_id ctor_id) (sgen_id ctor_id)
)
in
c_function ~return:"static void"
Expand All @@ -1528,7 +1530,8 @@ let codegen_type_def =
in
let codegen_eq =
let codegen_eq_test ctor_id ctyp =
c_return (sail_equal (sgen_ctyp_name ctyp) "op1.%s, op2.%s" (sgen_id ctor_id) (sgen_id ctor_id))
c_return
(sail_equal (sgen_ctyp_name ctyp) "op1.variants.%s, op2.variants.%s" (sgen_id ctor_id) (sgen_id ctor_id))
in
let rec codegen_eq_tests = function
| [] -> c_return (string "false")
Expand Down Expand Up @@ -1559,7 +1562,7 @@ let codegen_type_def =
(separate space [string "enum"; string ("kind_" ^ sgen_id id); string "kind" ^^ semi]
^^ hardline ^^ string "union" ^^ space
^^ surround 2 0 lbrace (separate_map (semi ^^ hardline) codegen_tu tus ^^ semi) rbrace
^^ semi
^^ space ^^ string "variants" ^^ semi
)
rbrace
^^ semi;
Expand Down
3 changes: 0 additions & 3 deletions test/c/run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ def no_valgrind():
def test_c(name, c_opts, sail_opts, valgrind, compiler='cc'):
banner('Testing {} with C options: {} Sail options: {} valgrind: {}'.format(name, c_opts, sail_opts, valgrind))
results = Results(name)
if compiler == 'c++':
# tl_pat.c:66:31: error: ‘zX::<unnamed union>::<unnamed struct>::zX’ has the same name as the class in which it is declared
results.expect_failure("tl_pat.sail", "same name as the class in which it is declared")
if valgrind and no_valgrind():
print('skipping because no valgrind found')
return results.finish()
Expand Down
1 change: 1 addition & 0 deletions test/c/union_variant_names.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
36 changes: 36 additions & 0 deletions test/c/union_variant_names.sail
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
default Order dec

$include <prelude.sail>
$include <string.sail>

// Ensure that unions with variants that match the union name work.
// In the past the generated C code was not compatible with C++ due
// to different rules around anonymous unions.

union n0 = {
n0 : int,
}

union n1 = {
n2 : int,
n1 : int,
}

// newtype is syntactic sugar for a single entry union.
newtype n3 = n3 : int

enum n4 = { n4 }

function main() -> unit = {
// Prevent dead-code elimination of the types.
// This is not very robust but it works for now.
let v0 = n0(1);
let v1 = n1(2);
let v3 = n3(3);
let v4 = n4;
let x : int = match v1 {
n2(d) => d + 10,
n1(d) => d + 20,
};
print_endline(dec_str(x));
}

0 comments on commit 68d3839

Please sign in to comment.