Skip to content

Commit

Permalink
Fix broken --precompiledLib switch (#3817)
Browse files Browse the repository at this point in the history
* replace non-serializable Member.Attributes with just AttributeFullNames

* make ValueKind.NumberConstant precompile-friendly (can be json-(de)serialized now)

* replace KindOfChar with TypeCast

* fix UInt128 / Int128 representation

* upd changelog

* removed trace of char number hack (wrong pattern match)

* restore char-as-number workaround until there's a better idea

* style fixes

* Removed CharNumber

* fix parameters with UoM break linking to precompiledLib

* docs: add a note about why we only keep the attribute name for `MemberRefInfo`

* chore: update Fable.AST changelog

---------

Co-authored-by: Nick Dunets <nick@chaldal.net>
Co-authored-by: ncave <777696+ncave@users.noreply.github.com>
Co-authored-by: Maxime Mangel <mangel.maxime@protonmail.com>
  • Loading branch information
4 people authored May 23, 2024
1 parent 9d3441a commit 6a8f4e4
Show file tree
Hide file tree
Showing 20 changed files with 369 additions and 228 deletions.
8 changes: 8 additions & 0 deletions src/Fable.AST/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Removed

* Remove `MemberRefInfo.Attributes` [GH-3817](https://github.com/fable-compiler/Fable/pull/3817) (by @DunetsNM)

### Added

* Add `MemberRefInfo.AttributeFullNames` [GH-3817](https://github.com/fable-compiler/Fable/pull/3817) (by @DunetsNM)

## 4.4.0 - 2024-02-13

### Changed
Expand Down
48 changes: 45 additions & 3 deletions src/Fable.AST/Fable.fs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ type MemberRefInfo =
IsInstance: bool
CompiledName: string
NonCurriedArgTypes: Type list option
Attributes: Attribute seq
// We only store the attributes fullname otherwise deserialization of precompiled files fails
// System.Text.Json is not able to deserialize the standard Attribute type because it is an interface
// More about it here: https://github.com/fable-compiler/Fable/pull/3817
AttributeFullNames: string list
}

type MemberRef =
Expand Down Expand Up @@ -482,6 +485,26 @@ type ArrayKind =
| MutableArray
| ImmutableArray

[<RequireQualifiedAccess>]
type NumberValue =
| Int8 of sbyte
| UInt8 of byte
| Int16 of int16
| UInt16 of System.UInt16
| Int32 of System.Int32
| UInt32 of System.UInt32
| Int64 of System.Int64
| UInt64 of System.UInt64
| Int128 of upper: System.UInt64 * lower: System.UInt64 // System.Int128
| UInt128 of upper: System.UInt64 * lower: System.UInt64 // System.UInt128
| BigInt of bigint
| NativeInt of nativeint
| UNativeInt of unativeint
| Float16 of System.Single // System.Half
| Float32 of System.Single
| Float64 of System.Double
| Decimal of System.Decimal

type ValueKind =
// The AST from F# compiler is a bit inconsistent with ThisValue and BaseValue.
// ThisValue only appears in constructors and not in instance members (where `this` is passed as first argument)
Expand All @@ -497,7 +520,7 @@ type ValueKind =
/// String interpolation with support for JS tagged templates
/// String parts length should always be values.Length + 1
| StringTemplate of tag: Expr option * parts: string list * values: Expr list
| NumberConstant of value: obj * kind: NumberKind * info: NumberInfo
| NumberConstant of value: NumberValue * info: NumberInfo
| RegexConstant of source: string * flags: RegexFlag list
| NewOption of value: Expr option * typ: Type * isStruct: bool
| NewArray of newKind: NewArrayKind * typ: Type * kind: ArrayKind
Expand All @@ -518,7 +541,26 @@ type ValueKind =
| CharConstant _ -> Char
| StringConstant _
| StringTemplate _ -> String
| NumberConstant(_, kind, info) -> Number(kind, info)
| NumberConstant(value, info) ->
match value with
| NumberValue.Int8 _ -> NumberKind.Int8
| NumberValue.UInt8 _ -> NumberKind.UInt8
| NumberValue.Int16 _ -> NumberKind.Int16
| NumberValue.UInt16 _ -> NumberKind.UInt16
| NumberValue.Int32 _ -> NumberKind.Int32
| NumberValue.UInt32 _ -> NumberKind.UInt32
| NumberValue.Int64 _ -> NumberKind.Int64
| NumberValue.UInt64 _ -> NumberKind.UInt64
| NumberValue.Int128 _ -> NumberKind.Int128
| NumberValue.UInt128 _ -> NumberKind.UInt128
| NumberValue.BigInt _ -> NumberKind.BigInt
| NumberValue.NativeInt _ -> NumberKind.NativeInt
| NumberValue.UNativeInt _ -> NumberKind.UNativeInt
| NumberValue.Float16 _ -> NumberKind.Float16
| NumberValue.Float32 _ -> NumberKind.Float32
| NumberValue.Float64 _ -> NumberKind.Float64
| NumberValue.Decimal _ -> NumberKind.Decimal
|> fun kind -> Number(kind, info)
| RegexConstant _ -> Regex
| NewOption(_, t, isStruct) -> Option(t, isStruct)
| NewArray(_, t, k) -> Array(t, k)
Expand Down
1 change: 1 addition & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

* [GH-3817](https://github.com/fable-compiler/Fable/pull/3817) [All] Fix broken --precompiledLib switch (#3818) (by @DunetsNM)
* [JS/TS] Fixed TimeSpan.FromMilliseconds (#3815) (by @ncave)
* [Python] Fixed quotation for union string cases (by @dbrattli)
* [Python] Fixed casing issues with identifiers and reflection info (#3811) (by @dbrattli)
Expand Down
32 changes: 15 additions & 17 deletions src/Fable.Transforms/Dart/Fable2Dart.fs
Original file line number Diff line number Diff line change
Expand Up @@ -719,22 +719,20 @@ module Util =
|> Expression.identExpression
|> getParts t parts

let transformNumberLiteral com r kind (x: obj) =
match kind, x with
| Dart.Replacements.DartInt, (:? char as x) -> Expression.integerLiteral (int64 x)
| Int8, (:? int8 as x) -> Expression.integerLiteral (int64 x)
| UInt8, (:? uint8 as x) -> Expression.integerLiteral (int64 x)
| Int16, (:? int16 as x) -> Expression.integerLiteral (int64 x)
| UInt16, (:? uint16 as x) -> Expression.integerLiteral (int64 x)
| Int32, (:? int32 as x) -> Expression.integerLiteral (x)
| UInt32, (:? uint32 as x) -> Expression.integerLiteral (int64 x)
| Int64, (:? int64 as x) -> Expression.integerLiteral (x)
| UInt64, (:? uint64 as x) -> Expression.integerLiteral (int64 x)
| Float32, (:? float32 as x) -> Expression.doubleLiteral (float x)
| Float64, (:? float as x) -> Expression.doubleLiteral (x)
| _ ->
$"Expected literal of type %A{kind} but got {x.GetType().FullName}"
|> addErrorAndReturnNull com r
let transformNumberLiteral com (r: Option<SourceLocation>) (v: Fable.NumberValue) =
match v with
| Fable.NumberValue.Int8 x -> Expression.integerLiteral (int64 x)
| Fable.NumberValue.UInt8 x -> Expression.integerLiteral (int64 x)
| Fable.NumberValue.Int16 x -> Expression.integerLiteral (int64 x)
| Fable.NumberValue.UInt16 x -> Expression.integerLiteral (int64 x)
| Fable.NumberValue.Int32 x -> Expression.integerLiteral (x)
| Fable.NumberValue.UInt32 x -> Expression.integerLiteral (int64 x)
| Fable.NumberValue.Int64 x -> Expression.integerLiteral (x)
| Fable.NumberValue.UInt64 x -> Expression.integerLiteral (int64 x)
| Fable.NumberValue.Float16 x -> Expression.doubleLiteral (float x)
| Fable.NumberValue.Float32 x -> Expression.doubleLiteral (float x)
| Fable.NumberValue.Float64 x -> Expression.doubleLiteral (x)
| _ -> $"Numeric literal is not supported: %A{v}" |> addErrorAndReturnNull com r

let transformTuple (com: IDartCompiler) ctx (args: Expression list) =
let tup = List.length args |> getTupleTypeIdent com ctx
Expand Down Expand Up @@ -777,7 +775,7 @@ module Util =

// Dart enums are limited as we cannot set arbitrary values or combine them as flags
// so for now we compile F# enums as integers
| Fable.NumberConstant(x, kind, _) -> transformNumberLiteral com r kind x |> resolveExpr returnStrategy
| Fable.NumberConstant(x, _) -> transformNumberLiteral com r x |> resolveExpr returnStrategy

| Fable.RegexConstant(source, flags) ->
let flagToArg =
Expand Down
14 changes: 9 additions & 5 deletions src/Fable.Transforms/Dart/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@ let coreModFor =
| BclKeyValuePair _ -> FableError "Cannot decide core module" |> raise

let makeLongInt com r t signed (x: uint64) =
let lowBits = NumberConstant(float (uint32 x), Float64, NumberInfo.Empty)
let highBits = NumberConstant(float (x >>> 32), Float64, NumberInfo.Empty)
let lowBits =
NumberConstant(NumberValue.Float64(float (uint32 x)), NumberInfo.Empty)

let highBits =
NumberConstant(NumberValue.Float64(float (x >>> 32)), NumberInfo.Empty)

let unsigned = BoolConstant(not signed)

let args =
Expand Down Expand Up @@ -544,7 +548,7 @@ let rec getZero (com: ICompiler) (ctx: Context) (t: Type) =
| String -> makeStrConst "" // Using empty string instead of null so Dart doesn't complain
| Number(BigInt, _) as t -> Helper.LibCall(com, "BigInt", "fromInt32", t, [ makeIntConst 0 ])
| Number(Decimal, _) as t -> makeIntConst 0 |> makeDecimalFromExpr com None t
| Number(kind, uom) -> NumberConstant(getBoxedZero kind, kind, uom) |> makeValue None
| Number(kind, uom) -> NumberConstant(NumberValue.GetZero kind, uom) |> makeValue None
| Builtin(BclTimeSpan | BclTimeOnly) -> getZeroTimeSpan t
| Builtin BclDateTime as t -> Helper.LibCall(com, "Date", "minValue", t, [])
| Builtin BclDateTimeOffset as t -> Helper.LibCall(com, "DateOffset", "minValue", t, [])
Expand All @@ -563,7 +567,7 @@ let getOne (com: ICompiler) (ctx: Context) (t: Type) =
| Boolean -> makeBoolConst true
| Number(BigInt, _) as t -> Helper.LibCall(com, "BigInt", "fromInt32", t, [ makeIntConst 1 ])
| Number(Decimal, _) as t -> makeIntConst 1 |> makeDecimalFromExpr com None t
| Number(kind, uom) -> NumberConstant(getBoxedOne kind, kind, uom) |> makeValue None
| Number(kind, uom) -> NumberConstant(NumberValue.GetOne kind, uom) |> makeValue None
| ListSingleton(CustomOp com ctx None t "get_One" [] e) -> e
| _ -> makeIntConst 1

Expand Down Expand Up @@ -2078,7 +2082,7 @@ let parseNum (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr op
| "IsInfinity", [ _ ] when isFloat ->
Helper.LibCall(com, "Double", "isInfinity", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
|> Some
| ("Parse" | "TryParse") as meth, str :: NumberConst(:? int as style, _, _) :: _ ->
| ("Parse" | "TryParse") as meth, str :: NumberConst(NumberValue.Int32 style, _) :: _ ->
let hexConst = int System.Globalization.NumberStyles.HexNumber
let intConst = int System.Globalization.NumberStyles.Integer

Expand Down
28 changes: 25 additions & 3 deletions src/Fable.Transforms/FSharp2Fable.Util.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1422,7 +1422,19 @@ module TypeHelpers =
// TODO: Check it's effectively measure?
// TODO: Raise error if we cannot get the measure fullname?
match tryDefinition genArgs[0] with
| Some(_, Some fullname) -> fullname
| Some(_, Some fullname) ->
// Not sure why, but when precompiling F# changes measure types to MeasureProduct<'M, MeasureOne>
match fullname with
| Types.measureProduct2 ->
match
(nonAbbreviatedType genArgs[0]).GenericArguments
|> Seq.map (tryDefinition >> Option.bind snd)
|> List.ofSeq
with
// TODO: generalize it to support aggregate units such as <m/s> or more complex
| [ Some measure; Some Types.measureOne ] -> measure
| _ -> fullname
| _ -> fullname
| _ -> Naming.unknown
else
Naming.unknown
Expand Down Expand Up @@ -2147,13 +2159,18 @@ module Util =
let fableMemberFunctionOrValue =
FsMemberFunctionOrValue(memb) :> Fable.MemberFunctionOrValue

let attributeFullNames =
fableMemberFunctionOrValue.Attributes
|> Seq.map (fun attr -> attr.Entity.FullName)
|> List.ofSeq

Fable.MemberRef(
FsEnt.Ref(ent),
{
CompiledName = memb.CompiledName
IsInstance = memb.IsInstanceMember
NonCurriedArgTypes = nonCurriedArgTypes
Attributes = fableMemberFunctionOrValue.Attributes
AttributeFullNames = attributeFullNames
}
)
| ent ->
Expand Down Expand Up @@ -2182,13 +2199,18 @@ module Util =
let fableMemberFunctionOrValue =
FsMemberFunctionOrValue(memb) :> Fable.MemberFunctionOrValue

let attributeFullNames =
fableMemberFunctionOrValue.Attributes
|> Seq.map (fun attr -> attr.Entity.FullName)
|> List.ofSeq

Fable.MemberRef(
FsEnt.Ref(ent),
{
CompiledName = memb.CompiledName
IsInstance = memb.IsInstanceMember
NonCurriedArgTypes = None
Attributes = fableMemberFunctionOrValue.Attributes
AttributeFullNames = attributeFullNames
}
)
| ent ->
Expand Down
45 changes: 22 additions & 23 deletions src/Fable.Transforms/Fable2Babel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1613,27 +1613,26 @@ module Util =
let values = values |> List.mapToArray (fun e -> com.TransformAsExpr(ctx, e))

StringTemplate(tag, List.toArray parts, values, r) |> Literal
| Fable.NumberConstant(x, kind, _) ->
match kind, x with
| Decimal, (:? decimal as x) -> JS.Replacements.makeDecimal com r value.Type x |> transformAsExpr com ctx
| BigInt, (:? bigint as x) -> Expression.bigintLiteral (string<bigint> x, ?loc = r)
| Int64, (:? int64 as x) -> Expression.bigintLiteral (string<int64> x, ?loc = r)
| UInt64, (:? uint64 as x) -> Expression.bigintLiteral (string<uint64> x, ?loc = r)
// | Int128, (:? System.Int128 as x) -> Expression.bigintLiteral(string x, ?loc=r)
// | UInt128, (:? System.UInt128 as x) -> Expression.bigintLiteral(string x, ?loc=r)
| NativeInt, (:? nativeint as x) -> Expression.bigintLiteral (string<nativeint> x, ?loc = r)
| UNativeInt, (:? unativeint as x) -> Expression.bigintLiteral (string<unativeint> x, ?loc = r)
| Int8, (:? int8 as x) -> Expression.numericLiteral (float x, ?loc = r)
| UInt8, (:? uint8 as x) -> Expression.numericLiteral (float x, ?loc = r)
| Int16, (:? int16 as x) -> Expression.numericLiteral (float x, ?loc = r)
| UInt16, (:? uint16 as x) -> Expression.numericLiteral (float x, ?loc = r)
| Int32, (:? int32 as x) -> Expression.numericLiteral (float x, ?loc = r)
| UInt32, (:? uint32 as x) -> Expression.numericLiteral (float x, ?loc = r)
// | Float16, (:? System.Half as x) -> Expression.numericLiteral(float x, ?loc=r)
| Float32, (:? float32 as x) -> Expression.numericLiteral (float x, ?loc = r)
| Float64, (:? float as x) -> Expression.numericLiteral (float x, ?loc = r)
| _, (:? char as x) -> Expression.numericLiteral (float x, ?loc = r)
| _ -> addErrorAndReturnNull com r $"Numeric literal is not supported: {x.GetType().FullName}"
| Fable.NumberConstant(v, _) ->
match v with
| Fable.NumberValue.Int8 x -> Expression.numericLiteral (float x, ?loc = r)
| Fable.NumberValue.UInt8 x -> Expression.numericLiteral (float x, ?loc = r)
| Fable.NumberValue.Int16 x -> Expression.numericLiteral (float x, ?loc = r)
| Fable.NumberValue.UInt16 x -> Expression.numericLiteral (float x, ?loc = r)
| Fable.NumberValue.Int32 x -> Expression.numericLiteral (float x, ?loc = r)
| Fable.NumberValue.UInt32 x -> Expression.numericLiteral (float x, ?loc = r)
| Fable.NumberValue.Int64 x -> Expression.bigintLiteral (string<int64> x, ?loc = r)
| Fable.NumberValue.UInt64 x -> Expression.bigintLiteral (string<uint64> x, ?loc = r)
// | Fable.NumberValue.Int128(u,l) -> Expression.bigintLiteral(string System.Int128(u,l), ?loc=r)
// | Fable.NumberValue.UInt128(u,l) -> Expression.bigintLiteral(string System.UInt128(u,l), ?loc=r)
| Fable.NumberValue.BigInt x -> Expression.bigintLiteral (string<bigint> x, ?loc = r)
| Fable.NumberValue.NativeInt x -> Expression.bigintLiteral (string<nativeint> x, ?loc = r)
| Fable.NumberValue.UNativeInt x -> Expression.bigintLiteral (string<unativeint> x, ?loc = r)
| Fable.NumberValue.Float16 x -> Expression.numericLiteral (float x, ?loc = r)
| Fable.NumberValue.Float32 x -> Expression.numericLiteral (float x, ?loc = r)
| Fable.NumberValue.Float64 x -> Expression.numericLiteral (float x, ?loc = r)
| Fable.NumberValue.Decimal x -> JS.Replacements.makeDecimal com r value.Type x |> transformAsExpr com ctx
| _ -> addErrorAndReturnNull com r $"Numeric literal is not supported: %A{v}"
| Fable.RegexConstant(source, flags) -> Expression.regExpLiteral (source, flags, ?loc = r)
| Fable.NewArray(newKind, typ, kind) ->
match newKind with
Expand Down Expand Up @@ -1914,8 +1913,8 @@ module Util =
match callInfo.MemberRef with
| Some(Fable.MemberRef(_, info)) ->
let hasParamObjectAttribute =
info.Attributes
|> Seq.tryFind (fun attr -> attr.Entity.FullName = Atts.paramObject)
info.AttributeFullNames
|> List.tryFind (fun attr -> attr = Atts.paramObject)
|> Option.isSome

if hasParamObjectAttribute then
Expand Down
8 changes: 4 additions & 4 deletions src/Fable.Transforms/FableTransforms.fs
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ module private Transforms =
| Value(BoolConstant a, _), Value(BoolConstant b, _) -> Some(a = b)
| Value(CharConstant a, _), Value(CharConstant b, _) -> Some(a = b)
| Value(StringConstant a, _), Value(StringConstant b, _) -> Some(a = b)
| Value(NumberConstant(a, _, _), _), Value(NumberConstant(b, _, _), _) -> Some(a = b)
| Value(NumberConstant(a, _), _), Value(NumberConstant(b, _), _) -> Some(a = b)
| Value(NewOption(None, _, _), _), Value(NewOption(None, _, _), _) -> Some true
| Value(NewOption(Some a, _, _), _), Value(NewOption(Some b, _, _), _) -> tryEqualsAtCompileTime a b
| _ -> None
Expand All @@ -440,9 +440,9 @@ module private Transforms =
| Value(StringConstant v1, r1), Value(StringConstant v2, r2) ->
Value(StringConstant(v1 + v2), addRanges [ r1; r2 ])
// Assume NumberKind and NumberInfo are the same
| Value(NumberConstant(:? int as v1, AST.Int32, NumberInfo.Empty), r1),
Value(NumberConstant(:? int as v2, AST.Int32, NumberInfo.Empty), r2) ->
Value(NumberConstant(v1 + v2, AST.Int32, NumberInfo.Empty), addRanges [ r1; r2 ])
| Value(NumberConstant(NumberValue.Int32 v1, NumberInfo.Empty), r1),
Value(NumberConstant(NumberValue.Int32 v2, NumberInfo.Empty), r2) ->
Value(NumberConstant(NumberValue.Int32(v1 + v2), NumberInfo.Empty), addRanges [ r1; r2 ])
| _ -> e

| Operation(Logical(AST.LogicalAnd, (Value(BoolConstant b, _) as v1), v2), [], _, _) ->
Expand Down
4 changes: 3 additions & 1 deletion src/Fable.Transforms/OverloadSuffix.fs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ let rec private getTypeFastFullName (genParams: IDictionary<_, _>) (t: Fable.Typ
let genArgs = genArgs |> Seq.mapToList (getTypeFastFullName genParams)
// Not sure why, but when precompiling F# changes measure types to MeasureProduct<'M, MeasureOne>
match tdef.FullName, genArgs with
| Types.measureProduct2, [ measure; Types.measureOne ] -> measure
| Types.measureProduct2, [ measure; Types.measureOne ] ->
// TODO: generalize it to support aggregate units such as <m/s> or more complex
measure
| _ ->
let genArgs = String.concat "," genArgs

Expand Down
Loading

0 comments on commit 6a8f4e4

Please sign in to comment.