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

WIP RFC FS-1092: Anonymous Type-tagged Union types #10896

Closed
wants to merge 48 commits into from

Conversation

dsyme
Copy link
Contributor

@dsyme dsyme commented Jan 19, 2021

@dsyme dsyme changed the title WIP RFC FS-1092: Prototyping Erased Union types WIP RFC FS-1092: Erased Union types Jan 19, 2021
@dsyme dsyme changed the base branch from main to feature/auto-widen January 19, 2021 14:29
@dsyme
Copy link
Contributor Author

dsyme commented Jan 19, 2021

I set up auto-flow from feature/auto-widen to this branch here: https://github.com/dotnet/roslyn-tools/blob/master/src/GitHubCreateMergePRs/config.xml

@Happypig375
Copy link
Member

Can we get the "Candidate for F# vNext" here?

@cartermp
Copy link
Contributor

The base of this is still an experiment (for example: it's a draft) and nowhere near the quality of other vNext-style PRs. Once design quirks are worked out I think we can proceed with labeling it as such. I expect quite a bit of churn in both places until we get at something that's considered reviewable.

@dsyme dsyme changed the title WIP RFC FS-1092: Erased Union types WIP RFC FS-1092: Anonymous Type-tagged Union types Jan 27, 2021
@dsyme
Copy link
Contributor Author

dsyme commented Jan 27, 2021

@Swoorup I updated this by merging in the base branch

@dsyme dsyme marked this pull request as draft January 27, 2021 15:18
Don Syme and others added 3 commits January 28, 2021 18:08
…ature/erased-unions

Merge feature/auto-widen to feature/erased-unions
…ature/erased-unions

Merge feature/auto-widen to feature/erased-unions
KevinRansom and others added 8 commits July 13, 2021 11:54
…ature/erased-unions

Merge feature/auto-widen to feature/erased-unions
…ature/erased-unions

Merge feature/auto-widen to feature/erased-unions
…ature/erased-unions

Merge feature/auto-widen to feature/erased-unions
…ature/erased-unions

Merge feature/auto-widen to feature/erased-unions
@chkn
Copy link
Contributor

chkn commented Aug 4, 2021

Looks like there is an issue when a function type is used as one of the union types:

> type IntOrFn = (int | unit -> int);;
type IntOrFn = (int | (unit -> int))

> let foo :IntOrFn = 5;;
val foo : IntOrFn = 5

> let foo :IntOrFn = fun () -> 5;;

  let foo :IntOrFn = fun () -> 5;;
  -------------------^^^^^^^^^^^

stdin(6,20): error FS0002: This function takes too many arguments, or is used in a context where a function is not expected

@chkn
Copy link
Contributor

chkn commented Aug 5, 2021

Also, flexible types seem to work for let bound functions (which is great), but not for static methods:

> type IntOrStr = (int | string);;
type IntOrStr = (int | string)

> let mer (fn : unit -> #IntOrStr) = fn();;
val mer : fn:(unit -> 'a) -> 'a when 'a :> IntOrStr

> type Foof = static member Boom(fn : unit -> #IntOrStr) = fn();;
Process terminated. Assertion failed.
Unexpected exception raised in compiler: buildGenParam: multiple base types
System.Exception: buildGenParam: multiple base types
   at FSharp.Compiler.AbstractIL.ILRuntimeWriter.buildGenParamsPass1b@1405.Invoke(Int32 i, ILGenericParameterDef gp) in /fsharp/src/fsharp/absil/ilreflect.fs:line 1414
   at FSharp.Compiler.AbstractIL.ILRuntimeWriter.buildGenParamsPass1b(cenv cenv, emEnv emEnv, Type[] genArgs, FSharpList`1 gps) in /fsharp/src/fsharp/absil/ilreflect.fs:line 1405
   at FSharp.Compiler.AbstractIL.ILRuntimeWriter.buildMethodPass2(cenv cenv, ILTypeRef tref, TypeBuilder typB, emEnv emEnv, ILMethodDef mdef) in /fsharp/src/fsharp/absil/ilreflect.fs:line 1544
   at FSharp.Compiler.AbstractIL.ILRuntimeWriter.emEnv@1819-2.Invoke(emEnv emEnv, ILMethodDef mdef) in /fsharp/src/fsharp/absil/ilreflect.fs:line 1819
   at Microsoft.FSharp.Collections.ArrayModule.Fold[T,TState](FSharpFunc`2 folder, TState state, T[] array) in /fsharp/src/fsharp/FSharp.Core/array.fs:line 901
   at FSharp.Compiler.AbstractIL.ILRuntimeWriter.buildTypeDefPass2(cenv cenv, FSharpList`1 nesting, emEnv emEnv, ILTypeDef tdef) in /fsharp/src/fsharp/absil/ilreflect.fs:line 1819
   at FSharp.Compiler.AbstractIL.ILRuntimeWriter.emEnv@1825-5.Invoke(emEnv emEnv, ILTypeDef tdef) in /fsharp/src/fsharp/absil/ilreflect.fs:line 1825
   at Microsoft.FSharp.Collections.ListModule.Fold[T,TState](FSharpFunc`2 folder, TState state, FSharpList`1 list) in /fsharp/src/fsharp/FSharp.Core/list.fs:line 221
   at FSharp.Compiler.AbstractIL.ILRuntimeWriter.buildTypeDefPass2(cenv cenv, FSharpList`1 nesting, emEnv emEnv, ILTypeDef tdef) in /fsharp/src/fsharp/absil/ilreflect.fs:line 1825
   at FSharp.Compiler.AbstractIL.ILRuntimeWriter.buildModuleTypePass2(cenv cenv, emEnv emEnv, ILTypeDef tdef) in /fsharp/src/fsharp/absil/ilreflect.fs:line 2019
   at FSharp.Compiler.AbstractIL.ILRuntimeWriter.emEnv@2032-9.Invoke(emEnv emEnv, ILTypeDef tdef) in /fsharp/src/fsharp/absil/ilreflect.fs:line 2032
   at Microsoft.FSharp.Collections.ListModule.Fold[T,TState](FSharpFunc`2 folder, TState state, FSharpList`1 list) in /fsharp/src/fsharp/FSharp.Core/list.fs:line 221
   at FSharp.Compiler.AbstractIL.ILRuntimeWriter.buildModuleFragment(cenv cenv, emEnv emEnv, AssemblyBuilder asmB, ModuleBuilder modB, ILModuleDef m) in /fsharp/src/fsharp/absil/ilreflect.fs:line 2032
   at FSharp.Compiler.AbstractIL.ILRuntimeWriter.emitModuleFragment(ILGlobals ilg, Boolean emitTailcalls, emEnv emEnv, AssemblyBuilder asmB, ModuleBuilder modB, ILModuleDef modul, Boolean debugInfo, FSharpFunc`2 resolveAssemblyRef, FSharpFunc`2 tryFindSysILTypeRef) in /fsharp/src/fsharp/absil/ilreflect.fs:line 2103
   at FSharp.Compiler.Interactive.Shell.FsiDynamicCompiler.ProcessCodegenResults(CompilationThreadToken ctok, ErrorLogger errorLogger, FsiDynamicCompilerState istate, IncrementalOptimizationEnv optEnv, TcState tcState, TcConfig tcConfig, FSharpList`1 prefixPath, Boolean showTypes, Boolean isIncrementalFragment, String fragName, FSharpList`1 declaredImpls, IlxAssemblyGenerator ilxGenerator, IlxGenResults codegenResults) in /fsharp/src/fsharp/fsi/fsi.fs:line 1159
   at FSharp.Compiler.Interactive.Shell.FsiDynamicCompiler.ProcessInputs(CompilationThreadToken ctok, ErrorLogger errorLogger, FsiDynamicCompilerState istate, FSharpList`1 inputs, Boolean showTypes, Boolean isIncrementalFragment, Boolean isInteractiveItExpr, FSharpList`1 prefixPath) in /fsharp/src/fsharp/fsi/fsi.fs:line 1257
   at FSharp.Compiler.Interactive.Shell.FsiDynamicCompiler.EvalParsedDefinitions(CompilationThreadToken ctok, ErrorLogger errorLogger, FsiDynamicCompilerState istate, Boolean showTypes, Boolean isInteractiveItExpr, FSharpList`1 defs) in /fsharp/src/fsharp/fsi/fsi.fs:line 1390
   at FSharp.Compiler.Interactive.Shell.clo@2212-597.Invoke(FsiDynamicCompilerState istate) in /fsharp/src/fsharp/fsi/fsi.fs:line 2224
   at FSharp.Compiler.Interactive.Shell.FsiInteractionProcessor.InteractiveCatch[a](ErrorLogger errorLogger, FSharpFunc`2 f, a istate) in /fsharp/src/fsharp/fsi/fsi.fs:line 2122
   at FSharp.Compiler.ErrorLogger.ErrorLoggerExtensions.ErrorLogger.EmitDiagnostic(ErrorLogger x, Exception exn, FSharpDiagnosticSeverity severity) in /fsharp/src/fsharp/ErrorLogger.fs:line 372
   at FSharp.Compiler.ErrorLogger.ErrorLoggerExtensions.ErrorLogger.ErrorRecovery(ErrorLogger x, Exception exn, Range m) in /fsharp/src/fsharp/ErrorLogger.fs:line 408
   at FSharp.Compiler.ErrorLogger.ErrorLoggerExtensions.ErrorLogger.StopProcessingRecovery(ErrorLogger x, Exception exn, Range m) in /fsharp/src/fsharp/ErrorLogger.fs:line 422
   at FSharp.Compiler.ErrorLogger.stopProcessingRecovery(Exception exn, Range m) in /fsharp/src/fsharp/ErrorLogger.fs:line 482
   at FSharp.Compiler.Interactive.Shell.FsiInteractionProcessor.InteractiveCatch[a](ErrorLogger errorLogger, FSharpFunc`2 f, a istate) in /fsharp/src/fsharp/fsi/fsi.fs:line 2122
   at FSharp.Compiler.Interactive.Shell.FsiInteractionProcessor.ExecInteraction(CompilationThreadToken ctok, TcConfig tcConfig, FsiDynamicCompilerState istate, ParsedScriptInteraction action, ErrorLogger errorLogger) in /fsharp/src/fsharp/fsi/fsi.fs:line 2212
   at FSharp.Compiler.Interactive.Shell.FsiInteractionProcessor.execParsedInteractions(CompilationThreadToken ctok, TcConfig tcConfig, FsiDynamicCompilerState istate, FSharpOption`1 action, ErrorLogger errorLogger, FSharpOption`1 lastResult, CancellationToken cancellationToken) in /fsharp/src/fsharp/fsi/fsi.fs:line 2357
   at FSharp.Compiler.Interactive.Shell.FsiInteractionProcessor.executeParsedInteractions(CompilationThreadToken ctok, TcConfig tcConfig, FsiDynamicCompilerState istate, FSharpOption`1 action, ErrorLogger errorLogger, FSharpOption`1 lastResult, CancellationToken cancellationToken) in /fsharp/src/fsharp/fsi/fsi.fs:line 2368
   at FSharp.Compiler.Interactive.Shell.clo@2398-599.Invoke(CompilationThreadToken ctok, TcConfig tcConfig, FsiDynamicCompilerState istate) in /fsharp/src/fsharp/fsi/fsi.fs:line 2399
   at FSharp.Compiler.Interactive.Shell.FsiInteractionProcessor.mainThreadProcessAction[a,b](a ctok, FSharpFunc`2 action, b istate) in /fsharp/src/fsharp/fsi/fsi.fs:line 2381
   at FSharp.Compiler.Interactive.Shell.FsiInteractionProcessor.mainThreadProcessParsedInteractions(CompilationThreadToken ctok, ErrorLogger errorLogger, FSharpOption`1 action, FsiDynamicCompilerState istate, CancellationToken cancellationToken) in /fsharp/src/fsharp/fsi/fsi.fs:line 2398
   at FSharp.Compiler.Interactive.Shell.res@2458-60.Invoke(CompilationThreadToken ctok, FsiDynamicCompilerState istate) in /fsharp/src/fsharp/fsi/fsi.fs:line 2458
   at FSharp.Compiler.Interactive.Shell.clo@2102-596.Invoke(Unit unitVar0) in /fsharp/src/fsharp/fsi/fsi.fs:line 2111
   at <StartupCode$FSharp-Compiler-Interactive-Settings>.$Fsiaux.FSharp-Compiler-Interactive-IEventLoop-Invoke@50-1.Invoke(Unit x) in /fsharp/src/fsharp/fsiaux.fs:line 50
   at <StartupCode$FSharp-Compiler-Interactive-Settings>.$Fsiaux.run@41-4.Invoke(FSharpFunc`2 f) in /fsharp/src/fsharp/fsiaux.fs:line 41
   at <StartupCode$FSharp-Compiler-Interactive-Settings>.$Fsiaux.run@39.Invoke(Unit unitVar0) in /fsharp/src/fsharp/fsiaux.fs:line 41
   at FSharp.Compiler.Interactive.SimpleEventLoop.FSharp.Compiler.Interactive.IEventLoop.Run() in /fsharp/src/fsharp/fsiaux.fs:line 48
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at FSharp.Compiler.Interactive.Shell.Utilities.callInstanceMethod0[a](Object obj, Type[] typeArgs, String nm) in /fsharp/src/fsharp/fsi/fsi.fs:line 131
   at Sample.FSharp.Compiler.Interactive.Main.evaluateSession@228-4.EventLoopRun() in /fsharp/src/fsharp/fsi/fsimain.fs:line 246
   at FSharp.Compiler.Interactive.Shell.runLoop@2707.Invoke(Unit unitVar0) in /fsharp/src/fsharp/fsi/fsi.fs:line 2712
   at FSharp.Compiler.Interactive.Shell.DriveFsiEventLoop(FsiEvaluationSessionHostConfig fsi, FsiConsoleOutput fsiConsoleOutput) in /fsharp/src/fsharp/fsi/fsi.fs:line 2728
   at FSharp.Compiler.Interactive.Shell.FsiEvaluationSession.Run() in /fsharp/src/fsharp/fsi/fsi.fs:line 3182
   at Sample.FSharp.Compiler.Interactive.Main.evaluateSession(String[] argv) in /fsharp/src/fsharp/fsi/fsimain.fs:line 298
   at Sample.FSharp.Compiler.Interactive.Main.MainMain(String[] argv) in /fsharp/src/fsharp/fsi/fsimain.fs:line 342
Abort trap: 6

@Swoorup
Copy link
Contributor

Swoorup commented Aug 5, 2021

I doubt flexible type should be used that way. I can't see it making sense, at least not right now, as generics wouldn't be supported in this feature.

@chkn
Copy link
Contributor

chkn commented Aug 5, 2021

@Swoorup this is the problem I'm trying to solve:

> type IntOrStr = (int | string);;
type IntOrStr = (int | string)

> let fn = fun () -> 5;;
val fn : unit -> int

> // with flexible types  
- let foo (fn : unit -> #IntOrStr) = fn();;
val foo : fn:(unit -> 'a) -> 'a when 'a :> IntOrStr

> foo fn;;
val it : int = 5

> // without flexible types  
- let bar (fn : unit -> IntOrStr) = fn();;
val bar : fn:(unit -> IntOrStr) -> IntOrStr

> bar fn;;

  bar fn;;
  ----^^

stdin(6,5): error FS0001: Type mismatch. Expecting a
    'unit -> IntOrStr'    
but given a
    'unit -> int'    
The type 'IntOrStr' does not match the type 'int'

@Swoorup
Copy link
Contributor

Swoorup commented Aug 9, 2021

@chkn It looks more like a problem for auto-widening (as future improvements). Ideally you shouldn't need flexible type for this case.

if not (ResizeArray.exists (isObjTy g) list) then
if isObjTy g pt then
list.Clear()
list.Add(pt)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This creates a compatibility consideration for libraries that publish erased union types. It might not be a concern, but I think it's important to be aware of:

Library1

type Foo = obj

Library2 (references Library1)

type Bar = (* ... *)
type Problematic = (Foo | Bar) // where `Foo` is from Library1

Due to this conditional, the Bar part of Problematic will be discarded, keeping only obj aliased through Foo. But if Library1 is later updated to change the type of Foo, any code compiled against the updated Library1 and old Library2 will find that the Problematic type no longer accepts Bars.

Copy link
Contributor

@Swoorup Swoorup Aug 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't that be same issue as the following:

Library1

type Foo = int

Library2 (references Library1)

let handle (foo: Foo) = 
   foo + 1

Later Library1 is updated to so that Foo is aliased to String? handle doesn't handle string. I don't know enough about how .net handles transitive dependencies in depth.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you'd definitely expect that sort of breakage with the Foo type. But I wouldn't expect that using Problematic with Bar would also be broken in that case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you would just recompile the code no in that case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, if you could. But it might be a dependency you don't have the source for

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see, how this is different to other issues that arise out of dependency version mismatch. You'd only have the issue if you publish a package using a version and then republish it using the same version, causing the breakage.

@En3Tho
Copy link
Contributor

En3Tho commented Aug 24, 2021

@Swoorup @chkn Do you guys need help with this?

@Swoorup
Copy link
Contributor

Swoorup commented Aug 24, 2021

@Swoorup @chkn Do you guys need help with this?

Pattern matching, (giving out warnings when matches are incomplete or overmatching) is an area that needs to be looked after. At the moment, it is just based on :? pattern match which doesn't do much. There are other tickets which are addressing areas of pattern matching, so probably best holding for now.

@dsyme dsyme deleted the branch feature/auto-widen October 8, 2021 14:14
@dsyme dsyme closed this Oct 8, 2021
@brettfo brettfo deleted the feature/erased-unions branch October 15, 2021 21:01
@Swoorup
Copy link
Contributor

Swoorup commented Oct 18, 2021

Any reasons for this being closed? @dsyme

@dsyme
Copy link
Contributor Author

dsyme commented Oct 18, 2021

Hmm looks like it got auto-closed when the branch it was targeting got deleted. I'll retarget to main

@dsyme
Copy link
Contributor Author

dsyme commented Oct 18, 2021

Actually looks like feature/erased-unions got deleted by mistake

@dsyme dsyme restored the feature/erased-unions branch October 18, 2021 12:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants