Skip to content
Open
Show file tree
Hide file tree
Changes from 11 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
3 changes: 3 additions & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.200.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

### Fixed

* Finish type inference for traits resolved during code generation: type parameters of the chosen method that the trait does not determine are inferred from their member constraints and defaulted as for a direct call. This fixes internal error FS0073 for methods such as `static member M<'X>(x: int) : int`, and programs that compiled into a `NotSupportedException` ("Dynamic invocation of ... is not supported") are now rejected at compile time. ([Issue #8690](https://github.com/dotnet/fsharp/issues/8690), [Issue #7384](https://github.com/dotnet/fsharp/issues/7384), [PR #20821](https://github.com/dotnet/fsharp/pull/20821))
* Fix struct state machines in generic code dropping type parameters that only their data type mentions, which code generation had replaced with `obj`. ([PR #20821](https://github.com/dotnet/fsharp/pull/20821))
* Fix `--pathmap` not producing path-independent binaries for portable PDBs: the debug directory was reserved from the original PDB path rather than the mapped one, so output retained padding proportional to the original path's length. ([PR #20509](https://github.com/dotnet/fsharp/pull/20509))
* Capture diagnostics emitted when transparent project checking finishes a project, including FS0247 for namespace-module name collisions. ([Issue #6036](https://github.com/dotnet/fsharp/issues/6036), [PR #20706](https://github.com/dotnet/fsharp/pull/20706))
* Fix false IDE type-mismatch diagnostics for interpolated strings with multiple formatted holes. ([Issue #20752](https://github.com/dotnet/fsharp/issues/20752), [PR #20775](https://github.com/dotnet/fsharp/pull/20775))
Expand Down Expand Up @@ -35,6 +37,7 @@

### Changed

* Revert the IlxGen fallback from [PR #19710](https://github.com/dotnet/fsharp/pull/19710) that silently defaulted type variables missing from the IL type environment. Code generation reports FS0073 again instead of emitting wrong IL, which had masked [Issue #20812](https://github.com/dotnet/fsharp/issues/20812). ([PR #20821](https://github.com/dotnet/fsharp/pull/20821))
* Remove the unused internal `--test:NoErrorText` switch and its argument-only diagnostic formatting. ([PR #20742](https://github.com/dotnet/fsharp/pull/20742))
* Assign seven previously preview-only features to [F# 11.2](https://fsharp.github.io/fsharp-compiler-docs/release-notes/Language.html#11.2), now the default language version. `default`, `latest`, and `latestmajor` select F# 11.2; explicit `11` and `11.0` retain F# 11.0 behavior. Preview SDK projects use the same compiler default unless `LangVersion` is explicitly set. `FromEndSlicing` remains preview-only. ([PR #20617](https://github.com/dotnet/fsharp/pull/20617))
* Add `SynExceptionDefnReprTrivia` with the range of the `exception` keyword to `SynExceptionDefnRepr`. ([Fantomas Issue #3483](https://github.com/fsprojects/fantomas/issues/3483), [PR #20612](https://github.com/dotnet/fsharp/pull/20612))
Expand Down
8 changes: 1 addition & 7 deletions src/Compiler/Checking/CheckDeclarations.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6102,13 +6102,7 @@ let ApplyDefaults (cenv: cenv) g denvAtEnd m moduleContents extraAttribs =

CanonicalizePartialInferenceProblem cenv.css denvAtEnd m unsolved

// The priority order comes from the order of declaration of the defaults in FSharp.Core.
for priority = 10 downto 0 do
unsolved |> List.iter (fun tp ->
if not tp.IsSolved then
// Apply the first default. If we're defaulting one type variable to another then
// the defaults will be propagated to the new type variable.
ApplyTyparDefaultAtPriority denvAtEnd cenv.css priority tp)
ApplyTyparDefaultsByPriority denvAtEnd cenv.css unsolved |> RaiseOperationResult

// OK, now apply defaults for any unsolved TyparStaticReq.HeadType or typars with SRTP (MayResolveMember) constraints
// Note: We also check for MayResolveMember constraints because some SRTP typars may not have StaticReq set
Expand Down
101 changes: 89 additions & 12 deletions src/Compiler/Checking/ConstraintSolver.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4406,8 +4406,8 @@ let SolveTypeAsError denv css m ty =
let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m denv
SolveTypeEqualsTypeKeepAbbrevs csenv 0 m NoTrace ty ty2 |> ignore

let ApplyTyparDefaultAtPriority denv css priority (tp: Typar) =
tp.Constraints |> List.iter (fun tpc ->
let private ApplyTyparDefaultAtPriority denv css priority (tp: Typar) =
tp.Constraints |> IterateD (fun tpc ->
match tpc with
| TyparConstraint.DefaultsTo(priority2, ty2, m) when priority2 = priority ->
let ty1 = mkTyparTy tp
Expand All @@ -4419,8 +4419,26 @@ let ApplyTyparDefaultAtPriority denv css priority (tp: Typar) =
(fun res ->
SolveTypeAsError denv css m ty1
ErrorD(ErrorFromApplyingDefault(css.g, denv, tp, ty2, res, m)))
|> RaiseOperationResult
| _ -> ())
else
CompleteD
| _ -> CompleteD)

/// Apply the 'default' constraints of the type variables, highest priority first. A default's priority is its
/// position from the end of its 'when' clause (see TcTyparConstraints), so FSharp.Core's operators try e.g.
/// 'default ^T2: ^T3' before 'default ^T1: int'. Defaulting one type variable to another propagates the defaults
/// to the other.
let ApplyTyparDefaultsByPriority denv css (tps: Typar list) =
[ 10..-1..0 ] |> IterateD (fun priority -> tps |> IterateD (ApplyTyparDefaultAtPriority denv css priority))

/// Choose a solution for a type parameter subject to its constraints and apply it by using a constraint.
let SolveTyparWithChosenSolution css denv tp =
let g = css.g
let amap = css.amap
let max, m = ChooseTyparSolutionAndRange g amap tp
let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m denv
PostponeOnFailedMemberConstraintResolution csenv NoTrace
(fun csenv -> SolveTyparEqualsType csenv 0 m NoTrace (mkTyparTy tp) max)
(fun err -> ErrorD(ErrorFromApplyingDefault(g, denv, tp, max, err, m)))

let CreateCodegenState tcVal g amap =
ConstraintSolverState.New(g, amap, InfoReader(g, amap), tcVal)
Expand All @@ -4439,6 +4457,70 @@ let SolveMemberConstraintForCodegen tcVal g amap m (traitInfo: TraitConstraintIn
else
SolveMemberConstraint csenv true PermitWeakResolution.Yes 0 m NoTrace traitInfo

/// Overload resolution freshens the chosen method's type parameters, and a codegen solve leaves those the trait
/// does not determine to whatever processes the code generated from the solution: inlining the method and solving
/// its nested traits. Once that is done, any still unsolved, e.g. 'X of 'static member M<'X>(x: int) : int', are
/// finished the way ApplyDefaults and SolveInternalUnknowns finish a file: their member constraints are solved,
/// then ApplyTyparDefaultsByPriority and SolveTyparWithChosenSolution apply. A trait over generic types belongs to
/// an inline template solved again at each call site, so its solution stays open.
let FinishCodegenTraitSolution tcVal g amap m (traitInfo: TraitConstraintInfo) =
let openTypars minst =
(freeInTypes CollectTypars minst).FreeTypars
|> Zset.elements
|> List.filter (fun tp -> tp.Rigidity <> TyparRigidity.Rigid)

let isGround () =
(freeInTypes CollectTypars (traitInfo.SupportTypes @ traitInfo.CompiledObjectAndArgumentTypes @ Option.toList traitInfo.CompiledReturnType))
.FreeTypars
.IsEmpty

// Nearly every solution has nothing open, so check that before creating a solver state.
match traitInfo.Solution with
| Some(FSMethSln(minst = minst))
| Some(ILMethSln(minst = minst)) when not (List.isEmpty (openTypars minst)) && isGround () ->
Comment thread
majocha marked this conversation as resolved.
Outdated
let css = CreateCodegenState tcVal g amap
let denv = DisplayEnv.Empty g
let unsolved () = openTypars minst

trackErrors {
// Unlike CanonicalizePartialInferenceProblem, which walks the constraints recorded in a solver state,
// canonicalize from the type parameters themselves: FreshenMethInfo copied their constraints onto them.
do!
IgnoreFailedMemberConstraintResolution
(fun () ->
let csenv =
{ MakeConstraintSolverEnv ContextInfo.NoContext css m denv with
ErrorOnFailedMemberConstraintResolution = true
}

RepeatWhileD 0 (fun ndeep ->
unsolved ()
|> AtLeastOneD (fun tp ->
tp.Constraints
|> AtLeastOneD (function
| TyparConstraint.MayResolveMember(traitInfo2, m2) when traitInfo2.Solution.IsNone ->
SolveMemberConstraint csenv true PermitWeakResolution.Yes ndeep m2 NoTrace traitInfo2
| _ -> ResultD false))))
ErrorD

do! ApplyTyparDefaultsByPriority denv css (unsolved ())

do!
unsolved ()
|> IterateD (fun tp -> if tp.IsSolved then CompleteD else SolveTyparWithChosenSolution css denv tp)

// Nothing later resolves what solving postponed, so run those checks now, as the end of checking does.
do!
[ yield! css.GetPostInferenceChecksPreDefaults(); yield! css.GetPostInferenceChecksFinal() ]
|> IterateD (fun check ->
try
check ()
CompleteD
with RecoverableException err ->
ErrorD err)
}
| _ -> CompleteD

/// Determine if a codegen witness for a trait will require witness args to be available, e.g. in generic code
let CodegenWitnessExprForTraitConstraintWillRequireWitnessArgs tcVal g amap m (traitInfo:TraitConstraintInfo) =
let traitInfo = GetTraitConstraintForCodegen g traitInfo
Expand Down Expand Up @@ -4478,6 +4560,7 @@ let CodegenWitnessesForTyparInstWith tcVal g amap m typars tyargs f =
traitInfo.Solution <- TryGetSelectedTraitSolution g (instTrait actualInst traitInfo)
| _ -> ()
let! _res = SolveTyparsEqualTypesAux csenv 0 m NoTrace tinst tyargs
do! traitInfos |> IterateD (FinishCodegenTraitSolution tcVal g amap m)
return [ for traitInfo in traitInfos -> f traitInfo (GenWitnessExprLambda amap g m traitInfo) ]
}

Expand All @@ -4489,21 +4572,15 @@ let CodegenWitnessArgForTraitConstraint tcVal g amap m traitInfo =
let traitInfo = GetTraitConstraintForCodegen g traitInfo
trackErrors {
let! _res = SolveMemberConstraintForCodegen tcVal g amap m traitInfo
do! FinishCodegenTraitSolution tcVal g amap m traitInfo
return GenWitnessExprLambda amap g m traitInfo
}

/// For some code like "let f() = ([] = [])", a free choice is made for a type parameter
/// for an interior type variable. This chooses a solution for a type parameter subject
/// to its constraints and applies that solution by using a constraint.
let ChooseTyparSolutionAndSolve css denv tp =
let g = css.g
let amap = css.amap
let max, m = ChooseTyparSolutionAndRange g amap tp
let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m denv
PostponeOnFailedMemberConstraintResolution csenv NoTrace
(fun csenv -> SolveTyparEqualsType csenv 0 m NoTrace (mkTyparTy tp) max)
(fun err -> ErrorD(ErrorFromApplyingDefault(g, denv, tp, max, err, m)))
|> RaiseOperationResult
SolveTyparWithChosenSolution css denv tp |> RaiseOperationResult

let CheckDeclaredTypars denv css m typars1 typars2 =
let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m denv
Expand Down
9 changes: 8 additions & 1 deletion src/Compiler/Checking/ConstraintSolver.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -368,12 +368,19 @@ val AddCxTyparDefaultsTo: DisplayEnv -> ConstraintSolverState -> range -> Contex

val SolveTypeAsError: DisplayEnv -> ConstraintSolverState -> range -> TType -> unit

val ApplyTyparDefaultAtPriority: DisplayEnv -> ConstraintSolverState -> priority: int -> Typar -> unit
/// Apply the 'default' constraints of the type variables, highest priority first.
val ApplyTyparDefaultsByPriority: DisplayEnv -> ConstraintSolverState -> Typar list -> OperationResult<unit>

/// Generate a witness expression if none is otherwise available, e.g. in legacy non-witness-passing code
val CodegenWitnessExprForTraitConstraint:
TcValF -> TcGlobals -> ImportMap -> range -> TraitConstraintInfo -> Expr list -> OperationResult<Expr option>

/// Finish the type parameters a codegen solution of a ground trait left open, once the code generated from the
/// solution has been processed: inlined and its nested traits solved. Callers of CodegenWitnessExprForTraitConstraint
/// call it with the same trait; the witness lambda functions call it themselves.
val FinishCodegenTraitSolution:
TcValF -> TcGlobals -> ImportMap -> range -> TraitConstraintInfo -> OperationResult<unit>

/// Determine if a codegen witness for a trait will require witness args to be available, e.g. in generic code
val CodegenWitnessExprForTraitConstraintWillRequireWitnessArgs:
TcValF -> TcGlobals -> ImportMap -> range -> TraitConstraintInfo -> OperationResult<bool>
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Checking/QuotationTranslator.fs
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,7 @@ and private ConvExprCore cenv (env : QuotationTranslationEnv) (expr: Expr) : Exp
| None ->
wfail(Error(FSComp.SR.crefQuotationsCantCallTraitMembers(), m))
| Some expr ->
ConstraintSolver.FinishCodegenTraitSolution cenv.tcVal g cenv.amap m traitInfo |> CommitOperationResult
ConvExpr cenv env expr

| _ ->
Expand Down
25 changes: 13 additions & 12 deletions src/Compiler/CodeGen/IlxGen.fs
Original file line number Diff line number Diff line change
Expand Up @@ -553,9 +553,6 @@ type TypeReprEnv
// Random value for post-hoc diagnostic analysis on generated tree *
uint16 666

/// Check if a type parameter is in the environment
member _.ContainsKey(tp: Typar) = reprs.ContainsKey(tp.Stamp)

/// Add an additional type parameter to the environment. If the parameter is a units-of-measure parameter
/// then it is ignored, since it doesn't correspond to a .NET type parameter.
member tyenv.AddOne(tp: Typar) =
Expand Down Expand Up @@ -736,15 +733,7 @@ and GenTypeAux cenv m (tyenv: TypeReprEnv) voidOK ptrsOK ty =
else
EraseClosures.mkILTyFuncTy cenv.ilxPubCloEnv

| TType_var(tp, _) ->
if tyenv.ContainsKey tp then
mkILTyvarTy tyenv[tp, m]
else
// Unsolved type variable not in the TypeReprEnv — can arise for inline SRTP
// functions where constraint resolution leaves phantom typars unsolved.
// Default the typar and generate the type for the default to avoid an ICE.
let defaultTy = TypeRelations.ChooseTyparSolution g cenv.amap tp
GenTypeAux cenv m tyenv voidOK ptrsOK defaultTy
| TType_var(tp, _) -> mkILTyvarTy tyenv[tp, m]

| TType_measure _ -> g.ilg.typ_Int32

Expand Down Expand Up @@ -6130,6 +6119,10 @@ and GenTraitCall (cenv: cenv) cgbuf eenv (traitInfo: TraitConstraintInfo, argExp
GenExpr cenv cgbuf eenv replacementExpr sequel
| Some expr ->
let expr = cenv.optimizeDuringCodeGen false expr

ConstraintSolver.FinishCodegenTraitSolution cenv.tcVal g cenv.amap m traitInfo
|> CommitOperationResult

GenExpr cenv cgbuf eenv expr sequel

//--------------------------------------------------------------------------
Expand Down Expand Up @@ -7758,6 +7751,14 @@ and GetIlxClosureFreeVars cenv m (thisVars: ValRef list) boxity eenv takenNames
| Local(_, _, Some(moreFtyvs, _)) -> unionFreeTyvars ftyvs moreFtyvs
| _ -> ftyvs)

// A struct state machine's type can mention type variables its MoveNext body does not, e.g. through its Data type
let cloFreeTyvars =
match boxity with
| ILBoxity.AsValue ->
(cloFreeTyvars, thisVars)
||> List.fold (fun ftyvs v -> unionFreeTyvars ftyvs (freeInType CollectTyparsNoCaching v.Type))
| ILBoxity.AsObject -> cloFreeTyvars

let cloFreeTyvars = cloFreeTyvars.FreeTypars |> Zset.elements

// Work out if the closure captures any witnesses.
Expand Down
6 changes: 5 additions & 1 deletion src/Compiler/Optimize/Optimizer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3479,7 +3479,11 @@ and OptimizeTraitCall cenv env (traitInfo, args, m) =
// Resolve the static overloading early (during the compulsory rewrite phase) so we can inline.
match ConstraintSolver.CodegenWitnessExprForTraitConstraint cenv.TcVal g cenv.amap m traitInfoForResolution args with

| OkResult (_, Some expr) -> OptimizeExpr cenv env expr
| OkResult (_, Some expr) ->
let res = OptimizeExpr cenv env expr
// Inlining the solution and solving its nested traits can determine its open type parameters, so finish after.
ConstraintSolver.FinishCodegenTraitSolution cenv.TcVal g cenv.amap m traitInfoForResolution |> RaiseOperationResult
res

// Resolution fails when optimizing generic code, ignore the failure
| _ ->
Expand Down
Loading
Loading