Skip to content

Commit

Permalink
[Rust] Support type extensions for external types (#3924)
Browse files Browse the repository at this point in the history
  • Loading branch information
ncave authored Oct 11, 2024
1 parent 44aee6e commit f8865f8
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 25 deletions.
1 change: 1 addition & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* [Rust] Added basic class inheritance support (by @ncave)
* [Rust] Added String.Replace(char, char) and test (by @ncave)
* [Rust] Support type extensions for external types (by @ncave)

## 4.22.0 - 2024-10-02

Expand Down
7 changes: 5 additions & 2 deletions src/Fable.Transforms/FSharp2Fable.Util.fs
Original file line number Diff line number Diff line change
Expand Up @@ -705,9 +705,12 @@ module Helpers =

let name, part =
match com.Options.Language, memb.DeclaringEntity with
| Rust, Some ent when memb.IsExtensionMember ->
// For Rust, add entity prefix to extension methods
cleanNameAsRustIdentifier name, part.Replace(cleanNameAsRustIdentifier)
| Rust, Some ent when ent.IsInterface && not memb.IsDispatchSlot ->
// For Rust, add entity prefix to default static interface members
cleanNameAsRustIdentifier name, part.Replace(cleanNameAsJsIdentifier)
cleanNameAsRustIdentifier name, part.Replace(cleanNameAsRustIdentifier)
| Rust, _ ->
// for Rust, no entity prefix for other members
memberNameAsRustIdentifier name part
Expand Down Expand Up @@ -2145,7 +2148,7 @@ module Util =

let memberName =
match com.Options.Language, memb.DeclaringEntity with
| Rust, Some ent when not memb.IsInstanceMember ->
| Rust, Some ent when not memb.IsInstanceMember || memb.IsExtensionMember ->
// for Rust, use the namespace for default static interface calls,
// for other non-instance calls, prefix with the full entity name
if ent.IsInterface && not memb.IsDispatchSlot && ent.FullName.Contains(".") then
Expand Down
63 changes: 40 additions & 23 deletions src/Fable.Transforms/Rust/Fable2Rust.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1245,14 +1245,14 @@ module Util =
else
mkGenericPathExpr (splitNameParts ident.Name) None

let isThisArgumentIdentExpr (expr: Fable.Expr) =
let isThisArgumentIdentExpr (ctx: Context) (expr: Fable.Expr) =
match expr with
| Fable.IdentExpr ident -> ident.IsThisArgument
| Fable.IdentExpr ident -> ident.IsThisArgument && ctx.IsAssocMember
| _ -> false

// let transformExprMaybeIdentExpr (com: IRustCompiler) ctx (expr: Fable.Expr) =
// match expr with
// | Fable.IdentExpr ident when ident.IsThisArgument ->
// | Fable.IdentExpr ident when ident.IsThisArgument && ctx.IsAssocMember ->
// // avoids the extra Lrc wrapping for self that transformIdentGet does
// transformIdent com ctx None id
// | _ -> com.TransformExpr(ctx, expr)
Expand Down Expand Up @@ -1607,7 +1607,7 @@ module Util =
let prepareRefForPatternMatch (com: IRustCompiler) ctx typ (name: string option) fableExpr =
let expr = com.TransformExpr(ctx, fableExpr)

if isThisArgumentIdentExpr fableExpr then
if isThisArgumentIdentExpr ctx fableExpr then
expr
elif (name.IsSome && isRefScoped ctx name.Value) || (isInRefType com typ) then
expr
Expand Down Expand Up @@ -2380,7 +2380,12 @@ module Util =
| Some thisArg, Fable.MemberImport memberRef ->
let memb = com.GetMember(memberRef)

if memb.IsInstance then
if memb.IsInstance && memb.IsExtension then
// extension method calls compiled as static method calls
let thisExpr = transformLeaveContext com ctx None thisArg
let callee = transformImport com ctx r t info genArgsOpt
mkCallExpr callee (thisExpr :: args)
elif memb.IsInstance then
let callee = transformCallee com ctx thisArg
mkMethodCallExpr info.Selector None callee args
else
Expand Down Expand Up @@ -2416,8 +2421,16 @@ module Util =
| _ ->
match callInfo.ThisArg, calleeExpr with
| Some thisArg, Fable.IdentExpr ident ->
let callee = transformCallee com ctx thisArg
mkMethodCallExpr ident.Name None callee args
match membOpt with
| Some memb when memb.IsExtension ->
// transform extension calls as static calls
let thisExpr = transformLeaveContext com ctx None thisArg
let callee = makeFullNamePathExpr ident.Name genArgsOpt
mkCallExpr callee (thisExpr :: args)
| _ ->
// other instance calls
let callee = transformCallee com ctx thisArg
mkMethodCallExpr ident.Name None callee args
| None, Fable.IdentExpr ident ->
let callee = makeFullNamePathExpr ident.Name genArgsOpt
mkCallExpr callee args
Expand Down Expand Up @@ -3439,7 +3452,7 @@ module Util =
| _ -> fieldIdents

let makeTypedParam (com: IRustCompiler) ctx (ident: Fable.Ident) returnType =
if ident.IsThisArgument then
if ident.IsThisArgument && ctx.IsAssocMember then
// is this a fluent API?
match ident.Type, shouldBeRefCountWrapped com ctx ident.Type with
| Fable.DeclaredType(entRef, genArgs), Some ptrType when ident.Type = returnType ->
Expand Down Expand Up @@ -3583,7 +3596,10 @@ module Util =
let scopedVarAttrs =
{
IsArm = false
IsRef = arg.IsThisArgument || isByRefType com arg.Type || ctx.IsParamByRefPreferred
IsRef =
arg.IsThisArgument && ctx.IsAssocMember
|| isByRefType com arg.Type
|| ctx.IsParamByRefPreferred
IsBox = false
IsFunc = false
UsageCount = countIdentUsage arg.Name body
Expand Down Expand Up @@ -3617,7 +3633,8 @@ module Util =
let label = tc.Label

let args =
args |> List.filter (fun arg -> not (arg.IsMutable || arg.IsThisArgument))
args
|> List.filter (fun arg -> not (arg.IsMutable || arg.IsThisArgument && ctx.IsAssocMember))

let mutArgs = args |> List.map (fun arg -> { arg with IsMutable = true })

Expand Down Expand Up @@ -3934,19 +3951,19 @@ module Util =
let macroItem = mkMacroItem attrs macroName [ expr ]
[ macroItem ]

let transformExtensionMethod (com: IRustCompiler) ctx (memb: Fable.MemberFunctionOrValue) (decl: Fable.MemberDecl) =
let argTypes = decl.Args |> List.map (fun arg -> arg.Type)

match argTypes with
| Fable.DeclaredType(entRef, genArgs) :: _ ->
let entName = getEntityFullName com ctx entRef
let memberItem = makeMemberItem com ctx true (decl, memb)
[ memberItem ] |> makeTraitImpls com ctx entName genArgs None
| _ -> []
// // not used anymore, as extension methods are compiled as normal module members
// let transformExtensionMethod (com: IRustCompiler) ctx (memb: Fable.MemberFunctionOrValue) (decl: Fable.MemberDecl) =
// let argTypes = decl.Args |> List.map (fun arg -> arg.Type)
// match argTypes with
// | Fable.DeclaredType(entRef, genArgs) :: _ ->
// let entName = getEntityFullName com ctx entRef
// let memberItem = makeMemberItem com ctx true (decl, memb)
// [ memberItem ] |> makeTraitImpls com ctx entName genArgs None
// | _ -> []

let transformModuleFunction (com: IRustCompiler) ctx (memb: Fable.MemberFunctionOrValue) (decl: Fable.MemberDecl) =
let name = Fable.Naming.splitLast decl.Name
//if name = "someProblematicFunction" then System.Diagnostics.Debugger.Break()

let isByRefPreferred =
memb.Attributes |> Seq.exists (fun a -> a.Entity.FullName = Atts.rustByRef)

Expand Down Expand Up @@ -4909,9 +4926,9 @@ module Util =
let memb = com.GetMember(decl.MemberRef)

let memberItems =
if memb.IsExtension && memb.IsInstance then
transformExtensionMethod com ctx memb decl
elif memb.IsValue then
// if memb.IsExtension && memb.IsInstance then
// transformExtensionMethod com ctx memb decl
if memb.IsValue then
transformModuleLetValue com ctx memb decl
else
transformModuleFunction com ctx memb decl
Expand Down

0 comments on commit f8865f8

Please sign in to comment.