@@ -52,6 +52,7 @@ type Context =
5252 CtorFieldExprs: Map < string , Beam .ErlExpr > // field name -> Erlang expr during constructor
5353 ClassFieldPrefix: bool // When true, NewRecord uses "field_" prefix for map keys (for explicit val field class ctors)
5454 CtorParamNames: Set < string > // Constructor parameter names (stored as class fields)
55+ CatchReasonVar: ( string * string ) option // (catch ident name, Erlang reason var name) for reraise support
5556 }
5657
5758/// Check if an entity ref refers to an interface type
@@ -759,88 +760,99 @@ let rec transformExpr (com: IBeamCompiler) (ctx: Context) (expr: Expr) : Beam.Er
759760 let reasonVar = $" Exn_reason_%d {ctr}"
760761 let identVar = capitalizeFirst ident.Name
761762
762- let ctx ' = { ctx with LocalVars = ctx.LocalVars.Add( ident.Name) }
763+ let ctx ' =
764+ { ctx with
765+ LocalVars = ctx.LocalVars.Add( ident.Name)
766+ CatchReasonVar = Some( ident.Name, reasonVar)
767+ }
768+
763769 let erlCatchBody = transformExpr com ctx' catchExpr
764770
765771 let catchBodyExprs =
766772 match erlCatchBody with
767773 | Beam.ErlExpr.Block es -> es
768774 | e -> [ e ]
769775
770- let reasonRef = Beam.ErlExpr.Variable reasonVar
776+ // Only generate the exception wrapping/binding when the catch body
777+ // actually references the exception identifier. This avoids unused
778+ // term warnings from the Erlang compiler.
779+ if containsIdentRef ident.Name catchExpr then
780+ let reasonRef = Beam.ErlExpr.Variable reasonVar
771781
772- let formatExpr =
773- Beam.ErlExpr.Call(
774- None,
775- " iolist_to_binary" ,
776- [
777- Beam.ErlExpr.Call(
778- Some " io_lib" ,
779- " format" ,
780- [
781- Beam.ErlExpr.Call(
782- None,
783- " binary_to_list" ,
784- [ Beam.ErlExpr.Literal( Beam.ErlLiteral.StringLit " ~p" ) ]
785- )
786- Beam.ErlExpr.List [ reasonRef ]
787- ]
788- )
789- ]
790- )
791-
792- let messageExpr =
793- Beam.ErlExpr.Case(
794- reasonRef,
795- [
796- {
797- Pattern = Beam.PWildcard
798- Guard = [ Beam.ErlExpr.Call( None, " is_binary" , [ reasonRef ]) ]
799- Body = [ reasonRef ]
800- }
801- {
802- Pattern = Beam.PWildcard
803- Guard = []
804- Body = [ formatExpr ]
805- }
806- ]
807- )
782+ let formatExpr =
783+ Beam.ErlExpr.Call(
784+ None,
785+ " iolist_to_binary" ,
786+ [
787+ Beam.ErlExpr.Call(
788+ Some " io_lib" ,
789+ " format" ,
790+ [
791+ Beam.ErlExpr.Call(
792+ None,
793+ " binary_to_list" ,
794+ [ Beam.ErlExpr.Literal( Beam.ErlLiteral.StringLit " ~p" ) ]
795+ )
796+ Beam.ErlExpr.List [ reasonRef ]
797+ ]
798+ )
799+ ]
800+ )
808801
809- // If reason is already a map (F# exception) or reference (class inheriting exn), use it directly.
810- // Otherwise wrap in #{message => ...} for .Message access.
811- let bindIdent =
812- Beam.ErlExpr.Match(
813- Beam.PVar identVar,
802+ let messageExpr =
814803 Beam.ErlExpr.Case(
815804 reasonRef,
816805 [
817806 {
818807 Pattern = Beam.PWildcard
819- Guard = [ Beam.ErlExpr.Call( None, " is_map" , [ reasonRef ]) ]
820- Body = [ reasonRef ]
821- }
822- {
823- Pattern = Beam.PWildcard
824- Guard = [ Beam.ErlExpr.Call( None, " is_reference" , [ reasonRef ]) ]
808+ Guard = [ Beam.ErlExpr.Call( None, " is_binary" , [ reasonRef ]) ]
825809 Body = [ reasonRef ]
826810 }
827811 {
828812 Pattern = Beam.PWildcard
829813 Guard = []
830- Body =
831- [
832- Beam.ErlExpr.Map
833- [
834- Beam.ErlExpr.Literal( Beam.ErlLiteral.AtomLit( Beam.Atom " message" )),
835- messageExpr
836- ]
837- ]
814+ Body = [ formatExpr ]
838815 }
839816 ]
840817 )
841- )
842818
843- Beam.ErlExpr.TryCatch( bodyExprs, reasonVar, [ bindIdent ] @ catchBodyExprs, afterExprs)
819+ // If reason is already a map (F# exception) or reference (class inheriting exn), use it directly.
820+ // Otherwise wrap in #{message => ...} for .Message access.
821+ let bindIdent =
822+ Beam.ErlExpr.Match(
823+ Beam.PVar identVar,
824+ Beam.ErlExpr.Case(
825+ reasonRef,
826+ [
827+ {
828+ Pattern = Beam.PWildcard
829+ Guard = [ Beam.ErlExpr.Call( None, " is_map" , [ reasonRef ]) ]
830+ Body = [ reasonRef ]
831+ }
832+ {
833+ Pattern = Beam.PWildcard
834+ Guard = [ Beam.ErlExpr.Call( None, " is_reference" , [ reasonRef ]) ]
835+ Body = [ reasonRef ]
836+ }
837+ {
838+ Pattern = Beam.PWildcard
839+ Guard = []
840+ Body =
841+ [
842+ Beam.ErlExpr.Map
843+ [
844+ Beam.ErlExpr.Literal( Beam.ErlLiteral.AtomLit( Beam.Atom " message" )),
845+ messageExpr
846+ ]
847+ ]
848+ }
849+ ]
850+ )
851+ )
852+
853+ Beam.ErlExpr.TryCatch( bodyExprs, reasonVar, [ bindIdent ] @ catchBodyExprs, afterExprs)
854+ else
855+ Beam.ErlExpr.TryCatch( bodyExprs, reasonVar, catchBodyExprs, afterExprs)
844856 | None, [] ->
845857 // No catch handler and no finalizer
846858 erlBody
@@ -1156,8 +1168,14 @@ let rec transformExpr (com: IBeamCompiler) (ctx: Context) (expr: Expr) : Beam.Er
11561168 | Extended( kind, _ range) ->
11571169 match kind with
11581170 | Throw( Some exprArg, _ typ) ->
1159- let erlExpr = transformExpr com ctx exprArg
1160- Beam.ErlExpr.Call( Some " erlang" , " error" , [ erlExpr ])
1171+ // For reraise: if the thrown expression references the catch ident,
1172+ // use the raw Erlang reason variable to preserve the original exception.
1173+ match exprArg, ctx.CatchReasonVar with
1174+ | IdentExpr ident, Some( catchIdentName, reasonVar) when ident.Name = catchIdentName ->
1175+ Beam.ErlExpr.Call( Some " erlang" , " error" , [ Beam.ErlExpr.Variable reasonVar ])
1176+ | _ ->
1177+ let erlExpr = transformExpr com ctx exprArg
1178+ Beam.ErlExpr.Call( Some " erlang" , " error" , [ erlExpr ])
11611179 | Throw( None, _ typ) ->
11621180 // Re-raise (should not normally happen outside catch context)
11631181 Beam.ErlExpr.Call(
@@ -3355,6 +3373,7 @@ let transformFile (com: Fable.Compiler) (file: File) : Beam.ErlModule =
33553373 CtorFieldExprs = Map.empty
33563374 ClassFieldPrefix = false
33573375 CtorParamNames = Set.empty
3376+ CatchReasonVar = None
33583377 }
33593378
33603379 let ctorNameRegistry = System.Collections.Generic.Dictionary< string, string>()
0 commit comments