Skip to content

Commit 0a74bdb

Browse files
committed
Create the function to check the 'this' arg in extension methods; Add the new test case
1 parent 9c6dc65 commit 0a74bdb

5 files changed

Lines changed: 66 additions & 13 deletions

File tree

src/Compiler/Checking/Expressions/CheckComputationExpressions.fs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ let inline noTailCall ceenv = { ceenv with tailCall = false }
6767

6868
let inline TryFindIntrinsicOrExtensionMethInfo collectionSettings (cenv: cenv) (env: TcEnv) m ad nm ty =
6969
AllMethInfosOfTypeInScope collectionSettings cenv.infoReader env.NameEnv (Some nm) ad IgnoreOverrides m ty
70+
|> List.filter (IsExtensionMethCompatibleWithTy cenv.g cenv.amap m ty)
7071

7172
/// Ignores an attribute
7273
let inline IgnoreAttribute _ = None
@@ -998,18 +999,7 @@ let inline addVarsToVarSpace (varSpace: LazyWithContext<Val list * TcEnv, range>
998999
)
9991000

10001001
let tryFindBuilderMethod (ceenv: ComputationExpressionContext<_>) (m: range) (methodName: string) =
1001-
let g = ceenv.cenv.g
1002-
let amap = ceenv.cenv.amap
10031002
TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult ceenv.cenv ceenv.env m ceenv.ad methodName ceenv.builderTy
1004-
|> List.filter (fun minfo ->
1005-
match minfo.GetObjArgTypes(amap, m, []) with
1006-
| thisTy :: _ ->
1007-
let ty1 = thisTy |> stripTyEqns g
1008-
let ty2 = ceenv.builderTy |> stripTyEqns g
1009-
1010-
TypeRelations.TypeFeasiblySubsumesType 0 g amap m ty1 TypeRelations.CanCoerce ty2
1011-
| _ ->
1012-
false)
10131003

10141004
let hasBuilderMethod ceenv m methodName =
10151005
tryFindBuilderMethod ceenv m methodName |> isNil |> not

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3121,6 +3121,7 @@ let BuildPossiblyConditionalMethodCall (cenv: cenv) env isMutable m isProp minfo
31213121

31223122
let TryFindIntrinsicOrExtensionMethInfo collectionSettings (cenv: cenv) (env: TcEnv) m ad nm ty =
31233123
AllMethInfosOfTypeInScope collectionSettings cenv.infoReader env.NameEnv (Some nm) ad IgnoreOverrides m ty
3124+
|> List.filter (IsExtensionMethCompatibleWithTy cenv.g cenv.amap m ty)
31243125

31253126
let TryFindFSharpSignatureInstanceGetterProperty (cenv: cenv) (env: TcEnv) m nm ty (sigTys: TType list) =
31263127
let g = cenv.g

src/Compiler/Checking/NameResolution.fs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,26 @@ let AllMethInfosOfTypeInScope collectionSettings infoReader nenv optFilter ad fi
762762
else
763763
intrinsic @ ExtensionMethInfosOfTypeInScope collectionSettings infoReader nenv ad optFilter LookupIsInstance.Ambivalent m ty
764764

765+
let IsExtensionMethCompatibleWithTy g amap m (ty: TType) (minfo: MethInfo) =
766+
not minfo.IsExtensionMember ||
767+
match minfo.GetObjArgTypes(amap, m, []) with
768+
| thisTy :: _ ->
769+
let ty1 = thisTy |> stripTyEqns g
770+
let ty2 = ty |> stripTyEqns g
771+
772+
match ty1, ty2 with
773+
| TType_var (tp1, _), _ ->
774+
tp1.Constraints |> List.exists (function
775+
| TyparConstraint.CoercesTo(targetCTy, _) ->
776+
let cTy = targetCTy |> stripTyEqns g
777+
TypeRelations.TypeFeasiblySubsumesType 0 g amap m cTy TypeRelations.CanCoerce ty2
778+
| _ -> false)
779+
| _, TType_var _ -> true
780+
| _ ->
781+
TypeRelations.TypeFeasiblySubsumesType 0 g amap m ty1 TypeRelations.CanCoerce ty2
782+
| _ ->
783+
true
784+
765785
//-------------------------------------------------------------------------
766786
// Helpers to do with building environments
767787
//-------------------------------------------------------------------------

src/Compiler/Checking/NameResolution.fsi

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,15 @@ val internal AllMethInfosOfTypeInScope:
689689
ty: TType ->
690690
MethInfo list
691691

692+
/// Check whether the 'this' argument of an extension method is compatible with the target type
693+
val internal IsExtensionMethCompatibleWithTy:
694+
g: TcGlobals ->
695+
amap: ImportMap ->
696+
m: range ->
697+
ty: TType ->
698+
minfo: MethInfo ->
699+
bool
700+
692701
/// Used to report an error condition where name resolution failed due to an indeterminate type
693702
exception internal IndeterminateType of range
694703

tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ComputationExpressions/CEExtensionMethodCapture.fs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ let ``CE doesn't capture an extension method with generic type``() =
5353
AsyncSeq(x)
5454
5555
[<Extension>]
56-
type PrivateExtensions =
56+
type PublicExtensions =
5757
[<Extension>]
5858
static member inline Run(this: #FooClass) =
5959
this
@@ -100,4 +100,37 @@ let ``CE captures a public extension method and procudes an error due to invalid
100100
"""
101101
|> asExe
102102
|> compile
103-
|> shouldFail
103+
|> shouldFail
104+
105+
// Deliberately trigger an error to ensure that a method is captured
106+
[<Fact>]
107+
let ``CE captures a public extension method with valid generic constrainted type and procudes an error due to invalid args``() =
108+
FSharp """
109+
open System.Runtime.CompilerServices
110+
111+
type AsyncSeq<'T>(i: 'T) =
112+
class
113+
let l = [i]
114+
member this.Data = l
115+
end
116+
117+
type AsyncSeqBuilder() =
118+
member _.Yield(x: 'T) : AsyncSeq<'T> =
119+
AsyncSeq(x)
120+
121+
[<Extension>]
122+
type PublicExtensions =
123+
[<Extension>]
124+
static member inline Run(this: #AsyncSeqBuilder, invalidArg: string) =
125+
this
126+
127+
let asyncSeq = AsyncSeqBuilder()
128+
129+
let xs : AsyncSeq<int> =
130+
asyncSeq {
131+
yield 1
132+
}
133+
"""
134+
|> asExe
135+
|> compile
136+
|> shouldFail

0 commit comments

Comments
 (0)