Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Rust] Support type extensions for external types #3924

Merged
merged 1 commit into from
Oct 11, 2024
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
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
Loading