-
Notifications
You must be signed in to change notification settings - Fork 853
Enhance the NameResolution System #19536
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
base: main
Are you sure you want to change the base?
Changes from all commits
682384b
b2e38f0
170baf1
a4d3388
f37ce5b
de7eed2
137dbf5
bec8732
76e285a
57a193b
5d0d1b1
9c6dc65
0a74bdb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -727,8 +727,11 @@ let SelectMethInfosFromExtMembers (infoReader: InfoReader) optFilter apparentTy | |
| ] | ||
|
|
||
| /// Query the available extension methods of a type (including extension methods for inherited types) | ||
| let ExtensionMethInfosOfTypeInScope (collectionSettings: ResultCollectionSettings) (infoReader: InfoReader) (nenv: NameResolutionEnv) optFilter isInstanceFilter m ty = | ||
| let extMemsDangling = SelectMethInfosFromExtMembers infoReader optFilter ty m nenv.eUnindexedExtensionMembers | ||
| let ExtensionMethInfosOfTypeInScope (collectionSettings: ResultCollectionSettings) (infoReader: InfoReader) (nenv: NameResolutionEnv) ad optFilter isInstanceFilter m ty = | ||
| let amap = infoReader.amap | ||
|
|
||
| let extMemsDangling = SelectMethInfosFromExtMembers infoReader optFilter ty m nenv.eUnindexedExtensionMembers | ||
|
|
||
| if collectionSettings = ResultCollectionSettings.AtMostOneResult && not (isNil extMemsDangling) then | ||
| extMemsDangling | ||
| else | ||
|
|
@@ -743,6 +746,9 @@ let ExtensionMethInfosOfTypeInScope (collectionSettings: ResultCollectionSetting | |
| | _ -> []) | ||
| extMemsDangling @ extMemsFromHierarchy | ||
| |> List.filter (fun minfo -> | ||
| let isAccesible = AccessibilityLogic.IsMethInfoAccessible amap m ad minfo | ||
|
|
||
| isAccesible && | ||
| match isInstanceFilter with | ||
| | LookupIsInstance.Ambivalent -> true | ||
| | LookupIsInstance.Yes -> minfo.IsInstance | ||
|
|
@@ -754,7 +760,27 @@ let AllMethInfosOfTypeInScope collectionSettings infoReader nenv optFilter ad fi | |
| if collectionSettings = ResultCollectionSettings.AtMostOneResult && not (isNil intrinsic) then | ||
| intrinsic | ||
| else | ||
| intrinsic @ ExtensionMethInfosOfTypeInScope collectionSettings infoReader nenv optFilter LookupIsInstance.Ambivalent m ty | ||
| intrinsic @ ExtensionMethInfosOfTypeInScope collectionSettings infoReader nenv ad optFilter LookupIsInstance.Ambivalent m ty | ||
|
|
||
| let IsExtensionMethCompatibleWithTy g amap m (ty: TType) (minfo: MethInfo) = | ||
| not minfo.IsExtensionMember || | ||
| match minfo.GetObjArgTypes(amap, m, []) with | ||
| | thisTy :: _ -> | ||
| let ty1 = thisTy |> stripTyEqns g | ||
| let ty2 = ty |> stripTyEqns g | ||
|
|
||
| match ty1, ty2 with | ||
| | TType_var (tp1, _), _ -> | ||
| tp1.Constraints |> List.exists (function | ||
| | TyparConstraint.CoercesTo(targetCTy, _) -> | ||
| let cTy = targetCTy |> stripTyEqns g | ||
| TypeRelations.TypeFeasiblySubsumesType 0 g amap m cTy TypeRelations.CanCoerce ty2 | ||
| | _ -> false) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Breaking Changes] When
An unconstrained (or non-CoercesTo-constrained) type variable should match any target type. Consider: | TType_var (tp1, _), _ ->
let coercesToConstraints =
tp1.Constraints |> List.choose (function
| TyparConstraint.CoercesTo(targetCTy, _) -> Some targetCTy
| _ -> None)
match coercesToConstraints with
| [] -> true // No CoercesTo constraint means it could match anything
| constraints ->
constraints |> List.exists (fun targetCTy ->
let cTy = targetCTy |> stripTyEqns g
TypeRelations.TypeFeasiblySubsumesType 0 g amap m cTy TypeRelations.CanCoerce ty2) |
||
| | _, TType_var _ -> true | ||
| | _ -> | ||
| TypeRelations.TypeFeasiblySubsumesType 0 g amap m ty1 TypeRelations.CanCoerce ty2 | ||
| | _ -> | ||
| true | ||
|
|
||
| //------------------------------------------------------------------------- | ||
| // Helpers to do with building environments | ||
|
|
@@ -1184,7 +1210,7 @@ let rec AddStaticContentOfTypeToNameEnv (g:TcGlobals) (amap: Import.ImportMap) a | |
| [| | ||
| // Extension methods | ||
| yield! | ||
| ExtensionMethInfosOfTypeInScope ResultCollectionSettings.AllResults infoReader nenv None LookupIsInstance.No m ty | ||
| ExtensionMethInfosOfTypeInScope ResultCollectionSettings.AllResults infoReader nenv ad None LookupIsInstance.No m ty | ||
| |> ChooseMethInfosForNameEnv g m ty | ||
|
|
||
| // Extension properties | ||
|
|
@@ -2827,7 +2853,7 @@ let rec ResolveLongIdentInTypePrim (ncenv: NameResolver) nenv lookupKind (resInf | |
| | _ -> | ||
| // lookup in-scope extension methods | ||
| // to keep in sync with the same expression in `| Some(MethodItem msets) when isLookupExpr` below | ||
| match ExtensionMethInfosOfTypeInScope ResultCollectionSettings.AllResults ncenv.InfoReader nenv optFilter isInstanceFilter m ty with | ||
| match ExtensionMethInfosOfTypeInScope ResultCollectionSettings.AllResults ncenv.InfoReader nenv ad optFilter isInstanceFilter m ty with | ||
| | [] -> success [resInfo, x, rest] | ||
| | methods -> | ||
| let extensionMethods = Item.MakeMethGroup(nm, methods) | ||
|
|
@@ -2841,7 +2867,7 @@ let rec ResolveLongIdentInTypePrim (ncenv: NameResolver) nenv lookupKind (resInf | |
| let minfos = msets |> ExcludeHiddenOfMethInfos g ncenv.amap m | ||
|
|
||
| // fold the available extension members into the overload resolution | ||
| let extensionMethInfos = ExtensionMethInfosOfTypeInScope ResultCollectionSettings.AllResults ncenv.InfoReader nenv optFilter isInstanceFilter m ty | ||
| let extensionMethInfos = ExtensionMethInfosOfTypeInScope ResultCollectionSettings.AllResults ncenv.InfoReader nenv ad optFilter isInstanceFilter m ty | ||
|
|
||
| success [resInfo, Item.MakeMethGroup (nm, minfos@extensionMethInfos), rest] | ||
|
|
||
|
|
@@ -2860,7 +2886,7 @@ let rec ResolveLongIdentInTypePrim (ncenv: NameResolver) nenv lookupKind (resInf | |
|
|
||
| if not (isNil pinfos) && isLookUpExpr then OneResult(success (resInfo, Item.Property (nm, pinfos, None), rest)) else | ||
|
|
||
| let minfos = ExtensionMethInfosOfTypeInScope ResultCollectionSettings.AllResults ncenv.InfoReader nenv optFilter isInstanceFilter m ty | ||
| let minfos = ExtensionMethInfosOfTypeInScope ResultCollectionSettings.AllResults ncenv.InfoReader nenv ad optFilter isInstanceFilter m ty | ||
|
|
||
| if not (isNil minfos) && isLookUpExpr then | ||
| success [resInfo, Item.MakeMethGroup (nm, minfos), rest] | ||
|
|
@@ -2898,7 +2924,7 @@ let rec ResolveLongIdentInTypePrim (ncenv: NameResolver) nenv lookupKind (resInf | |
| for p in ExtensionPropInfosOfTypeInScope ResultCollectionSettings.AllResults ncenv.InfoReader nenv None LookupIsInstance.Ambivalent ad m ty do | ||
| addToBuffer p.PropertyName | ||
|
|
||
| for m in ExtensionMethInfosOfTypeInScope ResultCollectionSettings.AllResults ncenv.InfoReader nenv None LookupIsInstance.Ambivalent m ty do | ||
| for m in ExtensionMethInfosOfTypeInScope ResultCollectionSettings.AllResults ncenv.InfoReader nenv ad None LookupIsInstance.Ambivalent m ty do | ||
| addToBuffer m.DisplayName | ||
|
|
||
| for p in GetIntrinsicPropInfosOfType ncenv.InfoReader None ad AllowMultiIntfInstantiations.No findFlag m ty do | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| module Conformance.BasicGrammarElements.UseBindExtensionMethodCapture | ||
|
|
||
| open Xunit | ||
| open FSharp.Test.Compiler | ||
|
|
||
| [<Fact>] | ||
| let ``Use binding doesn't capture an extension method with generic type``() = | ||
| FSharp """ | ||
| open System | ||
| open System.Runtime.CompilerServices | ||
|
|
||
| type FooClass() = class end | ||
|
|
||
| type Disposable() = | ||
| interface IDisposable with | ||
| member _.Dispose() = () | ||
|
|
||
| [<Extension>] | ||
| type PublicExtensions = | ||
| [<Extension>] | ||
| static member inline Dispose(this: #FooClass) = | ||
| this | ||
|
|
||
| let foo() = | ||
| use a = new Disposable() | ||
| () | ||
|
|
||
| foo() | ||
| """ | ||
| |> asExe | ||
| |> compile | ||
| |> shouldSucceed |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| module Conformance.Expressions.CEExtensionMethodCapture | ||
|
|
||
| open Xunit | ||
| open FSharp.Test.Compiler | ||
|
|
||
| [<Fact>] | ||
| let ``CE doesn't capture an extension method beyond the access domain``() = | ||
| FSharp """ | ||
| open System.Runtime.CompilerServices | ||
|
|
||
| type AsyncSeq<'T>(i: 'T) = | ||
| class | ||
| let l = [i] | ||
| member this.Data = l | ||
| end | ||
|
|
||
| type AsyncSeqBuilder() = | ||
| member _.Yield(x: 'T) : AsyncSeq<'T> = | ||
| AsyncSeq(x) | ||
|
|
||
| [<Extension>] | ||
| type PrivateExtensions = | ||
| [<Extension>] | ||
| static member inline private Run(this: AsyncSeqBuilder) = | ||
| this | ||
|
|
||
| let asyncSeq = AsyncSeqBuilder() | ||
|
|
||
| let xs : AsyncSeq<int> = | ||
| asyncSeq { | ||
| yield 1 | ||
| } | ||
| """ | ||
| |> asExe | ||
| |> compile | ||
| |> shouldSucceed | ||
|
|
||
| [<Fact>] | ||
| let ``CE doesn't capture an extension method with generic type``() = | ||
| FSharp """ | ||
| open System.Runtime.CompilerServices | ||
|
|
||
| type FooClass = class end | ||
|
|
||
| type AsyncSeq<'T>(i: 'T) = | ||
| class | ||
| let l = [i] | ||
| member this.Data = l | ||
| end | ||
|
|
||
| type AsyncSeqBuilder() = | ||
| member _.Yield(x: 'T) : AsyncSeq<'T> = | ||
| AsyncSeq(x) | ||
|
|
||
| [<Extension>] | ||
| type PublicExtensions = | ||
| [<Extension>] | ||
| static member inline Run(this: #FooClass) = | ||
| this | ||
|
|
||
| let asyncSeq = AsyncSeqBuilder() | ||
|
|
||
| let xs : AsyncSeq<int> = | ||
| asyncSeq { | ||
| yield 1 | ||
| } | ||
| """ | ||
| |> asExe | ||
| |> compile | ||
| |> shouldSucceed | ||
|
|
||
| // Deliberately trigger an error to ensure that a method is captured | ||
| [<Fact>] | ||
| let ``CE captures a public extension method and procudes an error due to invalid args``() = | ||
| FSharp """ | ||
| open System.Runtime.CompilerServices | ||
|
|
||
| type AsyncSeq<'T>(i: 'T) = | ||
| class | ||
| let l = [i] | ||
| member this.Data = l | ||
| end | ||
|
|
||
| type AsyncSeqBuilder() = | ||
| member _.Yield(x: 'T) : AsyncSeq<'T> = | ||
| AsyncSeq(x) | ||
|
|
||
| [<Extension>] | ||
| type PublicExtensions = | ||
| [<Extension>] | ||
| static member inline Run(this: AsyncSeqBuilder, invalidArg: string) = | ||
| this | ||
|
|
||
| let asyncSeq = AsyncSeqBuilder() | ||
|
|
||
| let xs : AsyncSeq<int> = | ||
| asyncSeq { | ||
| yield 1 | ||
| } | ||
| """ | ||
| |> asExe | ||
| |> compile | ||
| |> shouldFail | ||
|
|
||
| // Deliberately trigger an error to ensure that a method is captured | ||
| [<Fact>] | ||
| let ``CE captures a public extension method with valid generic constrainted type and procudes an error due to invalid args``() = | ||
| FSharp """ | ||
| open System.Runtime.CompilerServices | ||
|
|
||
| type AsyncSeq<'T>(i: 'T) = | ||
| class | ||
| let l = [i] | ||
| member this.Data = l | ||
| end | ||
|
|
||
| type AsyncSeqBuilder() = | ||
| member _.Yield(x: 'T) : AsyncSeq<'T> = | ||
| AsyncSeq(x) | ||
|
|
||
| [<Extension>] | ||
| type PublicExtensions = | ||
| [<Extension>] | ||
| static member inline Run(this: #AsyncSeqBuilder, invalidArg: string) = | ||
| this | ||
|
|
||
| let asyncSeq = AsyncSeqBuilder() | ||
|
|
||
| let xs : AsyncSeq<int> = | ||
| asyncSeq { | ||
| yield 1 | ||
| } | ||
| """ | ||
| |> asExe | ||
| |> compile | ||
| |> shouldFail | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Test Coverage] The existing tests all use [<Extension>]
type GenericExtensions =
[<Extension>]
static member inline Run(this: 'T) = thisThis would currently fail due to the |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering how is it already done in other cases? Is there a place that this function could be unified with?