Skip to content

Commit

Permalink
Updated FCS type constraints (#4016)
Browse files Browse the repository at this point in the history
  • Loading branch information
ncave authored Jan 19, 2025
1 parent 4f8930b commit 1cc7df5
Show file tree
Hide file tree
Showing 35 changed files with 574 additions and 171 deletions.
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"fantomas": {
"version": "6.3.16",
"version": "7.0.0",
"commands": [
"fantomas"
]
Expand Down
Binary file modified lib/fcs/FSharp.Compiler.Service.dll
Binary file not shown.
10 changes: 10 additions & 0 deletions lib/fcs/FSharp.Compiler.Service.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32571,6 +32571,11 @@ Get the signature for the value's XML documentation
Indicates a constraint that a type is a reference type
</summary>
</member>
<member name="P:FSharp.Compiler.Symbols.FSharpGenericParameterConstraint.IsNotSupportsNullConstraint">
<summary>
Indicates a constraint that a type doesn&apos;t support nullness
</summary>
</member>
<member name="P:FSharp.Compiler.Symbols.FSharpGenericParameterConstraint.IsNonNullableValueTypeConstraint">
<summary>
Indicates a constraint that a type is a non-Nullable value type
Expand Down Expand Up @@ -32611,6 +32616,11 @@ Get the signature for the value&apos;s XML documentation
Indicates a constraint that a type is a subtype of the given type
</summary>
</member>
<member name="P:FSharp.Compiler.Symbols.FSharpGenericParameterConstraint.IsAllowsRefStructConstraint">
<summary>
An anti-constraint indicating that ref structs (e.g. Span&lt;&gt;) are allowed here
</summary>
</member>
<member name="P:FSharp.Compiler.Symbols.FSharpGenericParameterConstraint.EnumConstraintTarget">
<summary>
Gets further information about an enumeration constraint
Expand Down
Binary file modified lib/fcs/FSharp.Core.dll
Binary file not shown.
Binary file modified lib/fcs/FSharp.DependencyManager.Nuget.dll
Binary file not shown.
6 changes: 5 additions & 1 deletion src/Fable.AST/Fable.fs
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,17 @@ type Constraint =
| HasMember of name: string * isStatic: bool
| CoercesTo of target: Type
| IsNullable
| IsNotNullable
| IsValueType
| IsReferenceType
| HasDefaultConstructor
| HasAllowsRefStruct
| HasComparison
| HasEquality
| IsUnmanaged
| IsEnum
| IsDelegate of argsType: Type * retType: Type
| IsEnum of baseType: Type
| SimpleChoice of types: Type list

type GenericParam =
abstract Name: string
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 @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* [All] Updated FCS to latest F# 9.0 (by @ncave)
* [All] Updated Fable-FCS to latest F# 9.0 (by @ncave)
* [All] Updated metadata to latest .NET 9.0 (by @ncave)
* [All] Updated FCS type constraints (by @ncave)

### Fixed

Expand Down
4 changes: 1 addition & 3 deletions src/Fable.Cli/Entry.fs
Original file line number Diff line number Diff line change
Expand Up @@ -504,9 +504,7 @@ let main argv =
let createLogger level =
use factory =
LoggerFactory.Create(fun builder ->
builder
.SetMinimumLevel(level)
.AddCustomConsole(fun options -> options.UseNoPrefixMsgStyle <- true)
builder.SetMinimumLevel(level).AddCustomConsole(fun options -> options.UseNoPrefixMsgStyle <- true)
|> ignore
)

Expand Down
4 changes: 1 addition & 3 deletions src/Fable.Cli/Pipeline.fs
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,7 @@ module Js =
// I believe for now we can ship it like that because it only deteriorate the source map
// it should not break them completely.
if srcLine <> 0 && srcCol <> 0 && file <> Some "unknown" then
mapGenerator
.Force()
.AddMapping(generated, original, source = sourcePath, ?name = displayName)
mapGenerator.Force().AddMapping(generated, original, source = sourcePath, ?name = displayName)

let compileFile (com: Compiler) (cliArgs: CliArgs) pathResolver isSilent (outPath: string) =
async {
Expand Down
1 change: 1 addition & 0 deletions src/Fable.Compiler/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* [All] Updated FCS to latest F# 9.0 (by @ncave)
* [All] Updated Fable-FCS to latest F# 9.0 (by @ncave)
* [All] Updated metadata to latest .NET 9.0 (by @ncave)
* [All] Updated FCS type constraints (by @ncave)

### Fixed

Expand Down
4 changes: 1 addition & 3 deletions src/Fable.Compiler/Globbing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ module Glob =

// Normalizes path for different OS
let inline normalizePath (path: string) =
path
.Replace('\\', Path.DirectorySeparatorChar)
.Replace('/', Path.DirectorySeparatorChar)
path.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar)

type private SearchOption =
| Directory of string
Expand Down
7 changes: 1 addition & 6 deletions src/Fable.Transforms/BabelPrinter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -843,12 +843,7 @@ module PrinterExtensions =
member printer.PrintRegExp(pattern, flags, loc) =
printer.Print("/", ?loc = loc)
// Note we cannot use Naming.escapeString because it will corrupt the regex pattern
printer.Print(
Regex
.Replace(pattern, @"(?<!\\)\/", @"\/")
.Replace("\r", @"\r")
.Replace("\n", @"\n")
)
printer.Print(Regex.Replace(pattern, @"(?<!\\)\/", @"\/").Replace("\r", @"\r").Replace("\n", @"\n"))

printer.Print("/")
printer.Print(flags)
Expand Down
6 changes: 3 additions & 3 deletions src/Fable.Transforms/Dart/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1014,9 +1014,9 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
// Strings
| ("PrintFormatToString" | "PrintFormatToStringThen" | "PrintFormat" | "PrintFormatLine" | "PrintFormatToError" | "PrintFormatLineToError" | "PrintFormatThen" | "PrintFormatToStringThenFail" | "PrintFormatToStringBuilder" | "PrintFormatToStringBuilderThen"), // Printf.kbprintf
_ -> fsFormat com ctx r t i thisArg args
| ("Failure" | "FailurePattern" | "LazyPattern" | "Lock" // lock
// | "NullArg" // nullArg
| "Using"), // using
| ("Failure" | "FailurePattern" | "LazyPattern" | "Lock" | "NullArg" | "Using"), _ ->
fsharpModule com ctx r t i thisArg args
| ("IsNull" | "IsNotNull" | "IsNullV" | "NonNull" | "NonNullV" | "NullMatchPattern" | "NullValueMatchPattern" | "NonNullQuickPattern" | "NonNullQuickValuePattern" | "WithNull" | "WithNullV" | "NullV" | "NullArgCheck"),
_ -> fsharpModule com ctx r t i thisArg args
// Exceptions
| "FailWith", [ msg ]
Expand Down
142 changes: 85 additions & 57 deletions src/Fable.Transforms/FSharp2Fable.Util.fs
Original file line number Diff line number Diff line change
Expand Up @@ -159,26 +159,47 @@ type FsGenParam(gen: FSharpGenericParameter) =
static member Constraint(c: FSharpGenericParameterConstraint) =
if c.IsCoercesToConstraint then
// It seems sometimes there are circular references so skip the constraints here
TypeHelpers.makeTypeWithConstraints false Map.empty c.CoercesToTarget
|> Fable.Constraint.CoercesTo
|> Some
let t = TypeHelpers.makeTypeWithConstraints false Map.empty c.CoercesToTarget
Fable.Constraint.CoercesTo t |> Some
elif c.IsMemberConstraint then
let d = c.MemberConstraintData // TODO: Full member signature hash?
Fable.Constraint.HasMember(d.MemberName, d.MemberIsStatic) |> Some
elif c.IsSupportsNullConstraint then
Some Fable.Constraint.IsNullable
elif c.IsRequiresDefaultConstructorConstraint then
Some Fable.Constraint.HasDefaultConstructor
elif c.IsNotSupportsNullConstraint then
Some Fable.Constraint.IsNotNullable
elif c.IsNonNullableValueTypeConstraint then
Some Fable.Constraint.IsValueType
elif c.IsReferenceTypeConstraint then
Some Fable.Constraint.IsReferenceType
elif c.IsRequiresDefaultConstructorConstraint then
Some Fable.Constraint.HasDefaultConstructor
elif c.IsAllowsRefStructConstraint then
Some Fable.Constraint.HasAllowsRefStruct
elif c.IsComparisonConstraint then
Some Fable.Constraint.HasComparison
elif c.IsEqualityConstraint then
Some Fable.Constraint.HasEquality
elif c.IsUnmanagedConstraint then
Some Fable.Constraint.IsUnmanaged
elif c.IsDelegateConstraint then
let d = c.DelegateConstraintData

let at =
TypeHelpers.makeTypeWithConstraints false Map.empty d.DelegateTupledArgumentType

let rt = TypeHelpers.makeTypeWithConstraints false Map.empty d.DelegateReturnType
Fable.Constraint.IsDelegate(at, rt) |> Some
elif c.IsEnumConstraint then
let t = TypeHelpers.makeTypeWithConstraints false Map.empty c.EnumConstraintTarget
Fable.Constraint.IsEnum t |> Some
elif c.IsSimpleChoiceConstraint then
let types =
c.SimpleChoices
|> Seq.map (TypeHelpers.makeTypeWithConstraints false Map.empty)
|> Seq.toList

Fable.Constraint.SimpleChoice types |> Some
else
None // TODO: Document these cases

Expand Down Expand Up @@ -1327,7 +1348,7 @@ module TypeHelpers =
match lambda with
| FSharpExprPatterns.Lambda(arg, body) -> ctx // leave lambda context as is
| _ ->
// if not a lambda, resolve the type args not alredy in context to Fable.Any
// if not a lambda, resolve the type args not already in context to Fable.Any
let newGenArgs = genArgs |> List.map (fun arg -> genParamName arg, Fable.Any)

let newCtxGenArgs =
Expand Down Expand Up @@ -1474,25 +1495,24 @@ module TypeHelpers =
else
Naming.unknown

let private makeRuntimeTypeWithMeasure (genArgs: IList<FSharpType>) fullName =
let genArgs = [ getMeasureFullName genArgs |> Fable.Measure ]

let private makeDeclaredType assemblyName genArgs fullName =
let entRef: Fable.EntityRef =
{
FullName = fullName
Path = Fable.CoreAssemblyName "System.Runtime"
Path = Fable.CoreAssemblyName assemblyName
}

Fable.DeclaredType(entRef, genArgs)

let private makeRuntimeType genArgs fullName =
makeDeclaredType "System.Runtime" genArgs fullName

let private makeFSharpCoreType genArgs fullName =
let entRef: Fable.EntityRef =
{
FullName = fullName
Path = Fable.CoreAssemblyName "FSharp.Core"
}
makeDeclaredType "FSharp.Core" genArgs fullName

Fable.DeclaredType(entRef, genArgs)
let private makeRuntimeTypeWithMeasure (genArgs: IList<FSharpType>) fullName =
let genArgs = [ getMeasureFullName genArgs |> Fable.Measure ]
makeRuntimeType genArgs fullName

let makeTypeFromDef withConstraints ctxTypeArgs (genArgs: IList<FSharpType>) (tdef: FSharpEntity) =
if tdef.IsArrayType then
Expand Down Expand Up @@ -1558,49 +1578,57 @@ module TypeHelpers =
Fable.DeclaredType(FsEnt.Ref tdef, genArgs)

let rec makeTypeWithConstraints withConstraints (ctxTypeArgs: Map<string, Fable.Type>) (NonAbbreviatedType t) =
// Generic parameter (try to resolve for inline functions)
if t.IsGenericParameter then
resolveGenParam withConstraints ctxTypeArgs t.GenericParameter
// Tuple
elif t.IsTupleType then
let genArgs =
makeTypeGenArgsWithConstraints withConstraints ctxTypeArgs t.GenericArguments

Fable.Tuple(genArgs, t.IsStructTupleType)
// Function
elif t.IsFunctionType then
let argType =
makeTypeWithConstraints withConstraints ctxTypeArgs t.GenericArguments[0]

let returnType =
makeTypeWithConstraints withConstraints ctxTypeArgs t.GenericArguments[1]

Fable.LambdaType(argType, returnType)
elif t.IsAnonRecordType then
let genArgs =
makeTypeGenArgsWithConstraints withConstraints ctxTypeArgs t.GenericArguments

let fields = t.AnonRecordTypeDetails.SortedFieldNames

let isStruct =
match t.BaseType with
| Some typ -> (getFsTypeFullName typ) = Types.valueType
| None -> false

Fable.AnonymousRecordType(fields, genArgs, isStruct)
elif t.HasTypeDefinition then
// No support for provided types when compiling FCS+Fable to JS
let typ =
// Generic parameter (try to resolve for inline functions)
if t.IsGenericParameter then
resolveGenParam withConstraints ctxTypeArgs t.GenericParameter
// Tuple
elif t.IsTupleType then
let genArgs =
makeTypeGenArgsWithConstraints withConstraints ctxTypeArgs t.GenericArguments

Fable.Tuple(genArgs, t.IsStructTupleType)
// Function
elif t.IsFunctionType then
let argType =
makeTypeWithConstraints withConstraints ctxTypeArgs t.GenericArguments[0]

let returnType =
makeTypeWithConstraints withConstraints ctxTypeArgs t.GenericArguments[1]

Fable.LambdaType(argType, returnType)
elif t.IsAnonRecordType then
let genArgs =
makeTypeGenArgsWithConstraints withConstraints ctxTypeArgs t.GenericArguments

let fields = t.AnonRecordTypeDetails.SortedFieldNames

let isStruct =
match t.BaseType with
| Some typ -> (getFsTypeFullName typ) = Types.valueType
| None -> false

Fable.AnonymousRecordType(fields, genArgs, isStruct)
elif t.HasTypeDefinition then
// No support for provided types when compiling FCS+Fable to JS
#if !FABLE_COMPILER
// TODO: Discard provided generated types too?
if t.TypeDefinition.IsProvidedAndErased then
Fable.Any
else
// TODO: Discard provided generated types too?
if t.TypeDefinition.IsProvidedAndErased then
Fable.Any
else
#endif
makeTypeFromDef withConstraints ctxTypeArgs t.GenericArguments t.TypeDefinition
elif t.IsMeasureType then
Fable.Measure ""
else
Fable.Any // failwithf "Unexpected non-declared F# type: %A" t
makeTypeFromDef withConstraints ctxTypeArgs t.GenericArguments t.TypeDefinition
elif t.IsMeasureType then
Fable.Measure ""
else
Fable.Any // failwithf "Unexpected non-declared F# type: %A" t

// TODO:
// if not t.IsGenericParameter && t.HasNullAnnotation // || t.IsNullAmbivalent
// then
// makeRuntimeType [ typ ] Types.nullable // represent it as Nullable<T>
// else typ
typ

let makeType (ctxTypeArgs: Map<string, Fable.Type>) t =
makeTypeWithConstraints true ctxTypeArgs t
Expand Down
14 changes: 11 additions & 3 deletions src/Fable.Transforms/OverloadSuffix.fs
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,23 @@ let private getConstraintHash genParams =
"")
+ "member "
+ name
| Fable.Constraint.CoercesTo t -> ":>" + getTypeFastFullName genParams t
| Fable.Constraint.CoercesTo t -> ":>" + (getTypeFastFullName genParams t)
| Fable.Constraint.IsNullable -> "null"
| Fable.Constraint.IsNotNullable -> "not null"
| Fable.Constraint.IsValueType -> "struct"
| Fable.Constraint.IsReferenceType -> "not struct"
| Fable.Constraint.IsUnmanaged -> "unmanaged"
| Fable.Constraint.HasDefaultConstructor -> "new"
| Fable.Constraint.HasAllowsRefStruct -> "allows ref struct"
| Fable.Constraint.HasComparison -> "comparison"
| Fable.Constraint.HasEquality -> "equality"
| Fable.Constraint.IsEnum -> "enum"
| Fable.Constraint.IsUnmanaged -> "unmanaged"
| Fable.Constraint.IsDelegate(at, rt) ->
"delegate"
+ ([ at; rt ] |> List.map (getTypeFastFullName genParams) |> String.concat "|")
| Fable.Constraint.IsEnum t -> "enum" + (getTypeFastFullName genParams t)
| Fable.Constraint.SimpleChoice types ->
"simple choice"
+ (types |> List.map (getTypeFastFullName genParams) |> String.concat "|")

let rec private getTypeFastFullName (genParams: IDictionary<_, _>) (t: Fable.Type) =
match t with
Expand Down
7 changes: 3 additions & 4 deletions src/Fable.Transforms/Python/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1106,9 +1106,8 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
// Strings
| ("PrintFormatToString" | "PrintFormatToStringThen" | "PrintFormat" | "PrintFormatLine" | "PrintFormatToError" | "PrintFormatLineToError" | "PrintFormatThen" | "PrintFormatToStringThenFail" | "PrintFormatToStringBuilder" | "PrintFormatToStringBuilderThen"), // bprintf
_ -> fsFormat com ctx r t i thisArg args
| ("Failure" | "FailurePattern" | "LazyPattern" | "NullArg" | "Using"), // nullArg
_ -> fsharpModule com ctx r t i thisArg args
| "Lock", _ -> // lock
| ("Failure" | "FailurePattern" | "LazyPattern" | "NullArg" | "Using"), _ -> fsharpModule com ctx r t i thisArg args
| "Lock", _ ->
Helper.LibCall(com, "util", "lock", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
// Exceptions
Expand Down Expand Up @@ -1941,7 +1940,7 @@ let results (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (_: Expr o
let nullables (com: ICompiler) (_: Context) r (t: Type) (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
match i.CompiledName, thisArg with
| ".ctor", None -> List.tryHead args
// | "get_Value", Some c -> Get(c, OptionValue, t, r) |> Some // Get(OptionValueOptionValue) doesn't do a null check
// | "get_Value", Some c -> Get(c, OptionValue, t, r) |> Some // Get(OptionValue) doesn't do a null check
| "get_Value", Some c -> Helper.LibCall(com, "option", "value", t, [ c ], ?loc = r) |> Some
| "get_HasValue", Some c -> Test(c, OptionTest true, r) |> Some
| _ -> None
Expand Down
6 changes: 4 additions & 2 deletions src/Fable.Transforms/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1243,7 +1243,9 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
// Strings
| ("PrintFormatToString" | "PrintFormatToStringThen" | "PrintFormat" | "PrintFormatLine" | "PrintFormatToError" | "PrintFormatLineToError" | "PrintFormatThen" | "PrintFormatToStringThenFail" | "PrintFormatToStringBuilder" | "PrintFormatToStringBuilderThen"), // Printf.kbprintf
_ -> fsFormat com ctx r t i thisArg args
| ("Failure" | "FailurePattern" | "LazyPattern" | "Lock" | "NullArg" | "Using"), // using
| ("Failure" | "FailurePattern" | "LazyPattern" | "Lock" | "NullArg" | "Using"), _ ->
fsharpModule com ctx r t i thisArg args
| ("IsNull" | "IsNotNull" | "IsNullV" | "NonNull" | "NonNullV" | "NullMatchPattern" | "NullValueMatchPattern" | "NonNullQuickPattern" | "NonNullQuickValuePattern" | "WithNull" | "WithNullV" | "NullV" | "NullArgCheck"),
_ -> fsharpModule com ctx r t i thisArg args
// Exceptions
| "FailWith", [ msg ]
Expand Down Expand Up @@ -2196,7 +2198,7 @@ let results (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (_: Expr o
let nullables (com: ICompiler) (_: Context) r (t: Type) (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
match i.CompiledName, thisArg with
| ".ctor", None -> List.tryHead args
// | "get_Value", Some c -> Get(c, OptionValue, t, r) |> Some // Get(OptionValueOptionValue) doesn't do a null check
// | "get_Value", Some c -> Get(c, OptionValue, t, r) |> Some // Get(OptionValue) doesn't do a null check
| "get_Value", Some c -> Helper.LibCall(com, "Option", "value", t, [ c ], ?loc = r) |> Some
| "get_HasValue", Some c -> Test(c, OptionTest true, r) |> Some
| _ -> None
Expand Down
Loading

0 comments on commit 1cc7df5

Please sign in to comment.