Skip to content

Commit ad6884a

Browse files
authored
[Beam] Unify receive overloads and fix String.Compare codegen (#4363)
1 parent 1029705 commit ad6884a

5 files changed

Lines changed: 21 additions & 20 deletions

File tree

src/Fable.Core/Fable.Core.BeamInterop.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ let importMember<'T> (path: string) : 'T = nativeOnly
2222
/// Imports all exports from an Erlang module
2323
let importAll<'T> (path: string) : 'T = nativeOnly
2424

25-
module Erlang =
25+
type Erlang =
2626
/// Selective receive with timeout. Returns Some(msg) on match, None on timeout.
2727
/// Each DU case maps to a receive clause with the case's CompiledName (or snake_case name)
2828
/// as the Erlang atom tag.
29-
let receive<'T> (timeoutMs: int) : 'T option = nativeOnly
29+
static member receive<'T>(timeoutMs: int) : 'T option = nativeOnly
3030
/// Blocking selective receive (no timeout). Blocks until a matching message arrives.
31-
let receiveForever<'T> () : 'T = nativeOnly
31+
static member receive<'T>() : 'T = nativeOnly

src/Fable.Transforms/Beam/Fable2Beam.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,8 +1684,8 @@ and transformReceive (com: IBeamCompiler) (ctx: Context) (emitInfo: EmitInfo) (t
16841684
let isForever = emitInfo.Macro = "__fable_beam_receive_forever__"
16851685

16861686
// Extract DU entity ref from type:
1687-
// receive<'T> : 'T option → Option(DeclaredType(ref, _), _)
1688-
// receiveForever<'T> : 'T → DeclaredType(ref, _)
1687+
// receive<'T>(timeoutMs) : 'T option → Option(DeclaredType(ref, _), _)
1688+
// receive<'T>() : 'T → DeclaredType(ref, _)
16891689
let entityRef =
16901690
match typ with
16911691
| Option(DeclaredType(ref, _), _) -> Some ref
@@ -1694,7 +1694,7 @@ and transformReceive (com: IBeamCompiler) (ctx: Context) (emitInfo: EmitInfo) (t
16941694

16951695
match entityRef with
16961696
| None ->
1697-
com.WarnOnlyOnce("Erlang.receive/receiveForever requires a DU type parameter")
1697+
com.WarnOnlyOnce("Erlang.receive requires a DU type parameter")
16981698
Beam.ErlExpr.Literal(Beam.ErlLiteral.AtomLit(Beam.Atom "undefined"))
16991699
| Some ref ->
17001700
match com.TryGetEntity(ref) with
@@ -1747,7 +1747,7 @@ and transformReceive (com: IBeamCompiler) (ctx: Context) (emitInfo: EmitInfo) (t
17471747
)
17481748

17491749
if isForever then
1750-
// receiveForever: no after clause, returns DU directly
1750+
// receive() (no timeout): no after clause, returns DU directly
17511751
Beam.ErlExpr.Receive(clauses, None)
17521752
else
17531753
// receive: has after clause, returns Option (Some = DU value, None = undefined)

src/Fable.Transforms/Beam/Replacements.fs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -902,13 +902,7 @@ let private strings
902902
// String.Compare
903903
| "Compare", None, [ a; b ] -> Helper.LibCall(com, "fable_string", "compare", t, [ a; b ]) |> Some
904904
| "Compare", None, [ a; b; compType ] ->
905-
// Dispatch on ignoreCase bool (true) or StringComparison enum (5=OrdinalIgnoreCase)
906-
emitExpr
907-
r
908-
t
909-
[ a; b; compType ]
910-
"(fun() -> case $2 of true -> fable_string:compare_ignore_case($0, $1); 5 -> fable_string:compare_ignore_case($0, $1); _ -> fable_string:compare($0, $1) end end)()"
911-
|> Some
905+
Helper.LibCall(com, "fable_string", "compare", t, [ a; b; compType ]) |> Some
912906
| "Compare", None, [ a; startA; b; startB; len ] ->
913907
// String.Compare(a, startA, b, startB, len) — substring comparison
914908
emitExpr
@@ -923,7 +917,7 @@ let private strings
923917
r
924918
t
925919
[ a; startA; b; startB; len; compType ]
926-
"(fun() -> case $5 of true -> fable_string:compare_ignore_case(binary:part($0, $1, $4), binary:part($2, $3, $4)); 5 -> fable_string:compare_ignore_case(binary:part($0, $1, $4), binary:part($2, $3, $4)); _ -> fable_string:compare(binary:part($0, $1, $4), binary:part($2, $3, $4)) end end)()"
920+
"fable_string:compare(binary:part($0, $1, $4), binary:part($2, $3, $4), $5)"
927921
|> Some
928922
// String.IsNullOrEmpty / IsNullOrWhiteSpace (static on System.String)
929923
| "IsNullOrEmpty", None, [ str ] -> Helper.LibCall(com, "fable_string", "is_null_or_empty", t, [ str ]) |> Some
@@ -4803,7 +4797,7 @@ let tryCall
48034797
| "Fable.Core.BeamInterop.Erlang" ->
48044798
match info.CompiledName, args with
48054799
| "receive", [ timeoutArg ] -> emitExpr r t [ timeoutArg ] "__fable_beam_receive__($0)" |> Some
4806-
| "receiveForever", _ -> emitExpr r t [] "__fable_beam_receive_forever__" |> Some
4800+
| "receive", [] -> emitExpr r t [] "__fable_beam_receive_forever__" |> Some
48074801
| _ -> None
48084802
// Testing assertions (used by our test framework)
48094803
| "Fable.Core.Testing.Assert" ->

src/fable-library-beam/fable_string.erl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
trim_start_chars/2,
3131
trim_end_chars/2,
3232
to_char_array/1, to_char_array/3,
33-
compare/2,
33+
compare/2, compare/3,
3434
compare_ignore_case/2,
3535
string_ctor_chars/1,
3636
string_ctor_char_count/2,
@@ -340,6 +340,13 @@ compare_ignore_case(A, B) ->
340340
LB = string:lowercase(B),
341341
compare(LA, LB).
342342

343+
%% compare/3 — dispatch on ignoreCase (bool) or StringComparison enum (int)
344+
compare(A, B, true) -> compare_ignore_case(A, B);
345+
compare(A, B, 1) -> compare_ignore_case(A, B); % CurrentCultureIgnoreCase
346+
compare(A, B, 3) -> compare_ignore_case(A, B); % InvariantCultureIgnoreCase
347+
compare(A, B, 5) -> compare_ignore_case(A, B); % OrdinalIgnoreCase
348+
compare(A, B, _) -> compare(A, B).
349+
343350
%% String constructors
344351

345352
string_ctor_chars(Chars) ->

tests/Beam/InteropTests.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,11 +300,11 @@ let ``test Erlang receive with multi-field DU case`` () =
300300
#endif
301301

302302
[<Fact>]
303-
let ``test Erlang receiveForever with self-send`` () =
303+
let ``test Erlang receive blocking with self-send`` () =
304304
#if FABLE_COMPILER
305-
// Send a message before calling receiveForever so it doesn't block
305+
// Send a message before calling receive so it doesn't block
306306
emitErlExpr () "erlang:self() ! ping"
307-
let msg = Erlang.receiveForever<RecvMsg> ()
307+
let msg = Erlang.receive<RecvMsg> ()
308308
match msg with
309309
| Ping -> equal 1 1
310310
| _ -> equal 0 1 // fail

0 commit comments

Comments
 (0)