diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index ce4f2c654..c7bc3c3f8 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [Beam] Fix dropped top-level side effects — multiple `ActionDeclaration` `main/0` functions are now merged (by @dbrattli) * [Beam] Fix `%%` escape in string interpolation producing double `%` instead of single (by @dbrattli) +* [Beam] Fix double-quoted atoms for uppercase `CompiledName` on DU cases (e.g. `''EXIT''` → `'EXIT'`) (by @dbrattli) +* [Beam] Fix cross-project imports producing self-recursive calls when source files share the same name (by @dbrattli) * [Beam] Support `Type.GetGenericArguments` and `Type.GetInterface` for reflection (by @dbrattli) * [TS] Correctly resolve type references for `TypeScriptTaggedUnion` (by @MangelMaxime and @jrwone0) * [TS] Expose optional `stack` property on `Exception` (by @MangelMaxime) diff --git a/src/Fable.Compiler/CHANGELOG.md b/src/Fable.Compiler/CHANGELOG.md index d69bd0448..ec176b098 100644 --- a/src/Fable.Compiler/CHANGELOG.md +++ b/src/Fable.Compiler/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [Beam] Fix dropped top-level side effects — multiple `ActionDeclaration` `main/0` functions are now merged (by @dbrattli) * [Beam] Fix `%%` escape in string interpolation producing double `%` instead of single (by @dbrattli) +* [Beam] Fix double-quoted atoms for uppercase `CompiledName` on DU cases (e.g. `''EXIT''` → `'EXIT'`) (by @dbrattli) +* [Beam] Fix cross-project imports producing self-recursive calls when source files share the same name (by @dbrattli) * [Beam] Support `Type.GetGenericArguments` and `Type.GetInterface` for reflection (by @dbrattli) * [TS] Correctly resolve type references for `TypeScriptTaggedUnion` (by @MangelMaxime and @jrwone0) * [Python] Fix `nonlocal`/`global` declarations generated inside `match/case` bodies causing `SyntaxError` (by @dbrattli) diff --git a/src/Fable.Transforms/Beam/Fable2Beam.fs b/src/Fable.Transforms/Beam/Fable2Beam.fs index 3720f6749..645f3672c 100644 --- a/src/Fable.Transforms/Beam/Fable2Beam.fs +++ b/src/Fable.Transforms/Beam/Fable2Beam.fs @@ -104,9 +104,8 @@ let private getUnionCaseAtomExpr (com: IBeamCompiler) (ref: EntityRef) (tag: int | Some name -> name | None -> sanitizeErlangName uci.Name - let atomStr = quoteErlangAtom atomName let isFieldless = uci.UnionCaseFields.IsEmpty - Some(atomStr, isFieldless) + Some(atomName, isFieldless) | None -> None let private getDecisionTarget (ctx: Context) targetIndex = @@ -125,9 +124,22 @@ let private matchTargetIdentAndValues idents values = /// Resolve the Erlang module name for an import, returning None if it's the current module. let resolveImportModuleName (com: IBeamCompiler) (importPath: string) = let name = moduleNameFromFile importPath - let currentModule = moduleNameFromFile com.CurrentFile - if name = currentModule then + // Resolve the import path to an absolute path so we can reliably compare + // against the current file. Import paths may be relative (e.g., "../Foo/Types.fs") + // or absolute. Without resolving, two different files with the same base name + // (e.g., Agent/Types.fs vs Reactive/Types.fs) would both produce module name "types" + // and the import would be incorrectly treated as a local (self-recursive) call. + let resolvedImportPath = + if System.IO.Path.IsPathRooted(importPath) then + System.IO.Path.GetFullPath(importPath) + else + let currentDir = System.IO.Path.GetDirectoryName(com.CurrentFile) + System.IO.Path.GetFullPath(System.IO.Path.Combine(currentDir, importPath)) + + let currentFileFull = System.IO.Path.GetFullPath(com.CurrentFile) + + if resolvedImportPath = currentFileFull then None else Some name @@ -1919,7 +1931,6 @@ and transformReceive (com: IBeamCompiler) (ctx: Context) (emitInfo: EmitInfo) (t | Some name -> name | None -> sanitizeErlangName uci.Name - let atomStr = quoteErlangAtom atomName let fields = uci.UnionCaseFields let fieldCount = fields.Length @@ -1928,20 +1939,20 @@ and transformReceive (com: IBeamCompiler) (ctx: Context) (emitInfo: EmitInfo) (t let pattern = if fieldCount = 0 then - Beam.ErlPattern.PLiteral(Beam.ErlLiteral.AtomLit(Beam.Atom atomStr)) + Beam.ErlPattern.PLiteral(Beam.ErlLiteral.AtomLit(Beam.Atom atomName)) else Beam.ErlPattern.PTuple( - Beam.ErlPattern.PLiteral(Beam.ErlLiteral.AtomLit(Beam.Atom atomStr)) + Beam.ErlPattern.PLiteral(Beam.ErlLiteral.AtomLit(Beam.Atom atomName)) :: (fieldVars |> List.map Beam.ErlPattern.PVar) ) // Build body: atom-tagged DU representation let body = if fieldCount = 0 then - Beam.ErlExpr.Literal(Beam.ErlLiteral.AtomLit(Beam.Atom atomStr)) // bare atom + Beam.ErlExpr.Literal(Beam.ErlLiteral.AtomLit(Beam.Atom atomName)) // bare atom else Beam.ErlExpr.Tuple( - Beam.ErlExpr.Literal(Beam.ErlLiteral.AtomLit(Beam.Atom atomStr)) + Beam.ErlExpr.Literal(Beam.ErlLiteral.AtomLit(Beam.Atom atomName)) :: (fieldVars |> List.map Beam.ErlExpr.Variable) ) diff --git a/tests/Beam/InteropTests.fs b/tests/Beam/InteropTests.fs index 5baa1a289..8f253827d 100644 --- a/tests/Beam/InteropTests.fs +++ b/tests/Beam/InteropTests.fs @@ -97,6 +97,7 @@ type RecvMsg = | Ping | [] CustomTag of value: int | DataMsg of x: int * y: string + | [] Exit of pid: int * reason: string #endif @@ -336,6 +337,20 @@ let ``test Erlang receive with multi-field DU case`` () = () #endif +[] +let ``test Erlang receive with uppercase CompiledName atom tag`` () = +#if FABLE_COMPILER + // Send {'EXIT', 1, <<"normal">>} to self — uppercase atoms must be single-quoted + emitErlExpr () "erlang:self() ! {'EXIT', 1, <<\"normal\">>}" + match Erlang.receive 1000 with + | Some(Exit(pid, reason)) -> + equal 1 pid + equal "normal" reason + | _ -> equal 0 1 // fail +#else + () +#endif + [] let ``test Erlang receive blocking with self-send`` () = #if FABLE_COMPILER