Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion lib/parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,25 @@ supertraits:

trait_item:
| sig_ = fn_sig SEMICOLON { TraitFn sig_ }
| f = fn_decl { TraitFnDefault f }
/* Trait method *default body* (issue #135 slice 5). Previously the two
forms were `fn_sig SEMICOLON` vs a whole `fn_decl`, which share the
long prefix `visibility? FN name params return_type?`; the LR conflict
resolved toward the signature form, so `pub fn ne(ref self, ...) ->
Bool { ... }` (stdlib/traits.affine) failed at the `{`. Left-factor:
parse `fn_sig` once, then branch on SEMICOLON vs fn_body — no shared-
prefix ambiguity (the two now differ purely on the next token).
Trait defaults don't use `total`/`where` (none in stdlib); fd_total
= false, fd_where = []. */
| sig_ = fn_sig body = fn_body
{ TraitFnDefault { fd_vis = sig_.fs_vis;
fd_total = false;
fd_name = sig_.fs_name;
fd_type_params = sig_.fs_type_params;
fd_params = sig_.fs_params;
fd_ret_ty = sig_.fs_ret_ty;
fd_eff = sig_.fs_eff;
fd_where = [];
fd_body = body } }
| TYPE name = ident kind = kind_annotation? default = type_default? SEMICOLON
{ TraitType { tt_name = name; tt_kind = kind; tt_default = default } }

Expand Down
23 changes: 23 additions & 0 deletions test/test_e2e.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3342,6 +3342,27 @@ let test_bare_effect_and_effect_row () =
fn q() -> Int / io { return 0; }
fn plain() -> Int { return 1; }|})

(* Issue #135 slice 5: trait method *default body* (left-factored vs the
signature form so the shared prefix no longer mis-resolves toward the
`;` form). `ref self` receiver + sig-only + assoc type unaffected. *)
let test_trait_default_body () =
Alcotest.(check bool) "trait fn with default body + ref self" true
(parse_check_passes
{|trait Eq {
pub fn eq(ref self, ref other: Self) -> Bool;
pub fn ne(ref self, ref other: Self) -> Bool {
return !self.eq(other);
}
}|})

let test_trait_sig_and_assoc_not_regressed () =
Alcotest.(check bool) "sig-only trait fn + associated type still parse" true
(parse_check_passes
{|trait Iter {
type Item;
pub fn next(mut self) -> Option<Int>;
}|})

let test_multi_arg_arrow () =
Alcotest.(check bool) "(A, B) -> C parses + typechecks" true
(parse_check_passes
Expand Down Expand Up @@ -3395,6 +3416,8 @@ let type_syntax_sugar_tests = [
Alcotest.test_case "xs[a:b]/[a:]/[:b]/[:] (#135 slice 2)" `Quick test_slice_full_range;
Alcotest.test_case "xs[0] index non-regressed (#135 sl.2)" `Quick test_slice_index_not_regressed;
Alcotest.test_case "effect E; + -> T / E (#135 slice 3)" `Quick test_bare_effect_and_effect_row;
Alcotest.test_case "trait default body + ref self (#135 sl5)" `Quick test_trait_default_body;
Alcotest.test_case "trait sig + assoc non-regressed (#135 sl5)" `Quick test_trait_sig_and_assoc_not_regressed;
Alcotest.test_case "(A, B) -> C (multi-arg arrow)" `Quick test_multi_arg_arrow;
Alcotest.test_case "(A, B) without arrow remains tuple" `Quick test_tuple_type_still_works;
]
Expand Down
Loading