From 7fa8028b75193478d7c01e7fa380442c4429681d Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 16 Jan 2023 17:23:41 +0100 Subject: [PATCH 1/8] Fixes #14613 --- src/Compiler/Checking/CheckExpressions.fs | 23 ++++----------- src/Compiler/Checking/InfoReader.fs | 14 +++++++-- src/Compiler/Checking/InfoReader.fsi | 4 +++ src/Compiler/Checking/MethodOverrides.fs | 4 +-- src/Compiler/Checking/MethodOverrides.fsi | 3 +- src/Compiler/Checking/NameResolution.fs | 3 +- .../ErrorMessages/ClassesTests.fs | 29 ++++++++++++++++++- 7 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 63dc5bcb595..4c1c47f53b0 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -10922,7 +10922,7 @@ and ComputeIsComplete enclosingDeclaredTypars declaredTypars ty = /// Determine if a uniquely-identified-abstract-slot exists for an override member (or interface member implementation) based on the information available /// at the syntactic definition of the member (i.e. prior to type inference). If so, we know the expected signature of the override, and the full slotsig /// it implements. Apply the inferred slotsig. -and ApplyAbstractSlotInference (cenv: cenv) (envinner: TcEnv) (baseValOpt: Val option) (argsAndRetTy, m, synTyparDecls, declaredTypars, memberId, tcrefObjTy, renaming, intfSlotTyOpt, valSynData, memberFlags: SynMemberFlags, attribs) = +and ApplyAbstractSlotInference (cenv: cenv) (envinner: TcEnv) (_: Val option) (argsAndRetTy, m, synTyparDecls, declaredTypars, memberId, tcrefObjTy, renaming, intfSlotTyOpt, valSynData, memberFlags: SynMemberFlags, attribs) = let g = cenv.g let ad = envinner.eAccessRights @@ -10951,7 +10951,10 @@ and ApplyAbstractSlotInference (cenv: cenv) (envinner: TcEnv) (baseValOpt: Val o match memberFlags.MemberKind with | SynMemberKind.Member -> let dispatchSlots, dispatchSlotsArityMatch = - GetAbstractMethInfosForSynMethodDecl(cenv.infoReader, ad, memberId, m, typToSearchForAbstractMembers, valSynData, memberFlags) + if g.langVersion.SupportsFeature(LanguageFeature.ErrorForNonVirtualMembersOverrides) then + GetAbstractMethInfosForSynMethodDecl(cenv.infoReader, ad, memberId, m, typToSearchForAbstractMembers, valSynData, memberFlags, DiscardOnFirstNonOverride) + else + GetAbstractMethInfosForSynMethodDecl(cenv.infoReader, ad, memberId, m, typToSearchForAbstractMembers, valSynData, memberFlags,IgnoreOverrides) let uniqueAbstractMethSigs = match dispatchSlots with @@ -10969,22 +10972,6 @@ and ApplyAbstractSlotInference (cenv: cenv) (envinner: TcEnv) (baseValOpt: Val o | _ -> [] // check that method to override is sealed is located at CheckOverridesAreAllUsedOnce (typrelns.fs) // We hit this case when it is ambiguous which abstract method is being implemented. - if g.langVersion.SupportsFeature(LanguageFeature.ErrorForNonVirtualMembersOverrides) then - // Checks if the declaring type inherits from a base class and is not FSharpObjModelTy - // Raises an error if we try to override an non virtual member with the same name in both - match baseValOpt with - | Some ttype when not(isFSharpObjModelTy g ttype.Type) -> - match stripTyEqns g ttype.Type with - | TType_app(tyconRef, _, _) -> - let ilMethods = tyconRef.ILTyconRawMetadata.Methods.AsList() - let nameOpt = ilMethods |> List.tryFind(fun id -> id.Name = memberId.idText) - match nameOpt with - | Some name when not name.IsVirtual -> - errorR(Error(FSComp.SR.tcNoMemberFoundForOverride(), memberId.idRange)) - | _ -> () - | _ -> () - | _ -> () - // If we determined a unique member then utilize the type information from the slotsig let declaredTypars = match uniqueAbstractMethSigs with diff --git a/src/Compiler/Checking/InfoReader.fs b/src/Compiler/Checking/InfoReader.fs index 27eb5c2547e..1f6208eb906 100644 --- a/src/Compiler/Checking/InfoReader.fs +++ b/src/Compiler/Checking/InfoReader.fs @@ -311,6 +311,9 @@ type FindMemberFlag = | IgnoreOverrides /// Get overrides instead of abstract slots when measuring whether a class/interface implements all its required slots. | PreferOverrides + /// Similar to "IgnoreOverrides", but filters the items bottom-to-top, + /// and discards all when finds first non-virtual member which hides one above it in hirearchy. + | DiscardOnFirstNonOverride /// The input list is sorted from most-derived to least-derived type, so any System.Object methods /// are at the end of the list. Return a filtered list where prior/subsequent members matching by name and @@ -561,8 +564,15 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this = /// Filter the overrides of methods or properties, either keeping the overrides or keeping the dispatch slots. static let FilterOverrides findFlag (isVirt:'a->bool, isNewSlot, isDefiniteOverride, isFinal, equivSigs, nmf:'a->string) items = let equivVirts x y = isVirt x && isVirt y && equivSigs x y + let filterDifiniteOverrides x = not (isDefiniteOverride x) - match findFlag with + match findFlag with + | DiscardOnFirstNonOverride -> + items + |> List.map (List.filter filterDifiniteOverrides) + |> ExcludeItemsInSuperTypesBasedOnEquivTestWithItemsInSubTypes nmf (fun newItem priorItem -> + isVirt newItem && not (isVirt priorItem) + ) | PreferOverrides -> items // For each F#-declared override, get rid of any equivalent abstract member in the same type @@ -583,7 +593,7 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this = items // Remove any F#-declared overrides. These may occur in the same type as the abstract member (unlike with .NET metadata) // Include any 'newslot' declared methods. - |> List.map (List.filter (fun x -> not (isDefiniteOverride x))) + |> List.map (List.filter filterDifiniteOverrides) // Remove any virtuals that are signature-equivalent to virtuals in subtypes, except for newslots // That is, keep if it's diff --git a/src/Compiler/Checking/InfoReader.fsi b/src/Compiler/Checking/InfoReader.fsi index da24ec26d9e..2d6489e70c9 100644 --- a/src/Compiler/Checking/InfoReader.fsi +++ b/src/Compiler/Checking/InfoReader.fsi @@ -89,6 +89,10 @@ type FindMemberFlag = /// Get overrides instead of abstract slots when measuring whether a class/interface implements all its required slots. | PreferOverrides + /// Similar to "IgnoreOverrides", but filters the items bottom-to-top, + /// and discards all when finds first non-virtual member which hides one above it in hirearchy. + | DiscardOnFirstNonOverride + /// An InfoReader is an object to help us read and cache infos. /// We create one of these for each file we typecheck. type InfoReader = diff --git a/src/Compiler/Checking/MethodOverrides.fs b/src/Compiler/Checking/MethodOverrides.fs index e317772dac4..58aa619bf04 100644 --- a/src/Compiler/Checking/MethodOverrides.fs +++ b/src/Compiler/Checking/MethodOverrides.fs @@ -927,7 +927,7 @@ let FinalTypeDefinitionChecksAtEndOfInferenceScope (infoReader: InfoReader, nenv /// Get the methods relevant to determining if a uniquely-identified-override exists based on the syntactic information /// at the member signature prior to type inference. This is used to pre-assign type information if it does -let GetAbstractMethInfosForSynMethodDecl(infoReader: InfoReader, ad, memberName: Ident, bindm, typToSearchForAbstractMembers, valSynData, memberFlags: SynMemberFlags) = +let GetAbstractMethInfosForSynMethodDecl(infoReader: InfoReader, ad, memberName: Ident, bindm, typToSearchForAbstractMembers, valSynData, memberFlags: SynMemberFlags, findFlag: FindMemberFlag) = let g = infoReader.g if not memberFlags.IsInstance && memberFlags.IsOverrideOrExplicitImpl then @@ -939,7 +939,7 @@ let GetAbstractMethInfosForSynMethodDecl(infoReader: InfoReader, ad, memberName: | _, Some(SlotImplSet(_, dispatchSlotsKeyed, _, _)) -> NameMultiMap.find memberName.idText dispatchSlotsKeyed |> List.map (fun reqdSlot -> reqdSlot.MethodInfo) | ty, None -> - GetIntrinsicMethInfosOfType infoReader (Some memberName.idText) ad AllowMultiIntfInstantiations.Yes IgnoreOverrides bindm ty + GetIntrinsicMethInfosOfType infoReader (Some memberName.idText) ad AllowMultiIntfInstantiations.Yes findFlag bindm ty let dispatchSlots = minfos |> List.filter (fun minfo -> minfo.IsDispatchSlot) let valReprSynArities = SynInfo.AritiesOfArgs valSynData diff --git a/src/Compiler/Checking/MethodOverrides.fsi b/src/Compiler/Checking/MethodOverrides.fsi index 1c671e6bdb5..d18a8cdc6f1 100644 --- a/src/Compiler/Checking/MethodOverrides.fsi +++ b/src/Compiler/Checking/MethodOverrides.fsi @@ -156,7 +156,8 @@ val GetAbstractMethInfosForSynMethodDecl: bindm: range * typToSearchForAbstractMembers: (TType * SlotImplSet option) * valSynData: SynValInfo * - memberFlags: SynMemberFlags -> + memberFlags: SynMemberFlags * + findFlag: FindMemberFlag -> MethInfo list * MethInfo list /// Get the properties relevant to determining if a uniquely-identified-override exists based on the syntactic information diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index c5ace8603a6..6bc0b3e73b7 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -3868,7 +3868,8 @@ let ResolveExprDotLongIdentAndComputeRange (sink: TcResultsSink) (ncenv: NameRes match findFlag, item with | FindMemberFlag.PreferOverrides, _ | _, NonOverridable() -> item, itemRange, false - | FindMemberFlag.IgnoreOverrides, _ -> + | FindMemberFlag.IgnoreOverrides, _ + | FindMemberFlag.DiscardOnFirstNonOverride, _ -> let _, item, _, itemRange = resolveExpr FindMemberFlag.PreferOverrides item, itemRange, true diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs index eeb3ab1d371..97d9a20ea29 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs @@ -542,4 +542,31 @@ type C() = |> withDiagnostics [ (Error 855, Line 7, Col 19, Line 7, Col 21, "No abstract or interface member was found that corresponds to this override") (Error 855, Line 9, Col 19, Line 9, Col 21, "No abstract or interface member was found that corresponds to this override") - ] \ No newline at end of file + ] + + [] + let ``Virtual member was found among virtual and non-virtual overloads with lang preview`` () = + let CSLib = + CSharp """ +public class A +{ + public void M1(int i) { } + public virtual void M1(string s) { } +} + """ |> withName "CSLib" + + let app = + FSharp """ +module ClassTests + +type Over () = + inherit A () + + override _.M1 (s: string) = () + """ + + app + |> withReferences [CSLib] + |> withLangVersionPreview + |> compile + |> shouldSucceed \ No newline at end of file From 215726db2792f5c8498846a7f9d7b683e8be5dd2 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 16 Jan 2023 19:22:21 +0100 Subject: [PATCH 2/8] Address PR comments; Added couple more tests --- src/Compiler/Checking/InfoReader.fs | 9 +-- .../ErrorMessages/ClassesTests.fs | 56 +++++++++++++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/Compiler/Checking/InfoReader.fs b/src/Compiler/Checking/InfoReader.fs index 1f6208eb906..ec2a5b5bb99 100644 --- a/src/Compiler/Checking/InfoReader.fs +++ b/src/Compiler/Checking/InfoReader.fs @@ -564,16 +564,17 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this = /// Filter the overrides of methods or properties, either keeping the overrides or keeping the dispatch slots. static let FilterOverrides findFlag (isVirt:'a->bool, isNewSlot, isDefiniteOverride, isFinal, equivSigs, nmf:'a->string) items = let equivVirts x y = isVirt x && isVirt y && equivSigs x y - let filterDifiniteOverrides x = not (isDefiniteOverride x) + let filterDifiniteOverride = List.filter(not << isDefiniteOverride) match findFlag with | DiscardOnFirstNonOverride -> items - |> List.map (List.filter filterDifiniteOverrides) + |> List.map filterDifiniteOverride |> ExcludeItemsInSuperTypesBasedOnEquivTestWithItemsInSubTypes nmf (fun newItem priorItem -> + equivSigs newItem priorItem && isVirt newItem && not (isVirt priorItem) ) - | PreferOverrides -> + | PreferOverrides -> items // For each F#-declared override, get rid of any equivalent abstract member in the same type // This is because F# abstract members with default overrides give rise to two members with the @@ -593,7 +594,7 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this = items // Remove any F#-declared overrides. These may occur in the same type as the abstract member (unlike with .NET metadata) // Include any 'newslot' declared methods. - |> List.map (List.filter filterDifiniteOverrides) + |> List.map filterDifiniteOverride // Remove any virtuals that are signature-equivalent to virtuals in subtypes, except for newslots // That is, keep if it's diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs index 97d9a20ea29..490f3787644 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs @@ -544,6 +544,62 @@ type C() = (Error 855, Line 9, Col 19, Line 9, Col 21, "No abstract or interface member was found that corresponds to this override") ] + [] + let ``Virtual members were found with multiple types in hierarchy with different overloads langversionPreview`` () = + let CSLib = + CSharp """ +public class A +{ + public virtual void M1(string s) { } +} + +public class B : A +{ + public virtual void M1(int i) { } +} + """ |> withName "CSLib" + + let app = + FSharp """ +module ClassTests +type C() = + inherit B () + override _.M1 (i: string) = () + override _.M1 (i: int) = () + """ |> withReferences [CSLib] + app + |> withLangVersionPreview + |> compile + |> shouldSucceed + + [] + let ``Virtual member was found with multiple types in hierarchy with different overloads langversionPreview`` () = + let CSLib = + CSharp """ +public class A +{ + public virtual void M1(string s) { } +} + +public class B : A +{ + public void M1(int i) { } +} + """ |> withName "CSLib" + + let app = + FSharp """ +module ClassTests +type C() = + inherit B () + override _.M1 (i: string) = () + """ |> withReferences [CSLib] + app + |> withLangVersionPreview + |> compile + |> shouldSucceed + + [] let ``Virtual member was found among virtual and non-virtual overloads with lang preview`` () = let CSLib = From 487cad4b5e0c9f13fbdc4bf159adbb3ef65d22c2 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 16 Jan 2023 19:23:48 +0100 Subject: [PATCH 3/8] Typos --- src/Compiler/Checking/InfoReader.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Checking/InfoReader.fs b/src/Compiler/Checking/InfoReader.fs index ec2a5b5bb99..324eb96570f 100644 --- a/src/Compiler/Checking/InfoReader.fs +++ b/src/Compiler/Checking/InfoReader.fs @@ -564,12 +564,12 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this = /// Filter the overrides of methods or properties, either keeping the overrides or keeping the dispatch slots. static let FilterOverrides findFlag (isVirt:'a->bool, isNewSlot, isDefiniteOverride, isFinal, equivSigs, nmf:'a->string) items = let equivVirts x y = isVirt x && isVirt y && equivSigs x y - let filterDifiniteOverride = List.filter(not << isDefiniteOverride) + let filterDefiniteOverride = List.filter(not << isDefiniteOverride) match findFlag with | DiscardOnFirstNonOverride -> items - |> List.map filterDifiniteOverride + |> List.map filterDefiniteOverride |> ExcludeItemsInSuperTypesBasedOnEquivTestWithItemsInSubTypes nmf (fun newItem priorItem -> equivSigs newItem priorItem && isVirt newItem && not (isVirt priorItem) @@ -594,7 +594,7 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this = items // Remove any F#-declared overrides. These may occur in the same type as the abstract member (unlike with .NET metadata) // Include any 'newslot' declared methods. - |> List.map filterDifiniteOverride + |> List.map filterDefiniteOverride // Remove any virtuals that are signature-equivalent to virtuals in subtypes, except for newslots // That is, keep if it's From cc57946133cc81dc3a969fdd2c698317f99aae36 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 16 Jan 2023 19:24:20 +0100 Subject: [PATCH 4/8] Typos --- src/Compiler/Checking/InfoReader.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Checking/InfoReader.fs b/src/Compiler/Checking/InfoReader.fs index 324eb96570f..e17e566c750 100644 --- a/src/Compiler/Checking/InfoReader.fs +++ b/src/Compiler/Checking/InfoReader.fs @@ -564,12 +564,12 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this = /// Filter the overrides of methods or properties, either keeping the overrides or keeping the dispatch slots. static let FilterOverrides findFlag (isVirt:'a->bool, isNewSlot, isDefiniteOverride, isFinal, equivSigs, nmf:'a->string) items = let equivVirts x y = isVirt x && isVirt y && equivSigs x y - let filterDefiniteOverride = List.filter(not << isDefiniteOverride) + let filterDefiniteOverrides = List.filter(not << isDefiniteOverride) match findFlag with | DiscardOnFirstNonOverride -> items - |> List.map filterDefiniteOverride + |> List.map filterDefiniteOverrides |> ExcludeItemsInSuperTypesBasedOnEquivTestWithItemsInSubTypes nmf (fun newItem priorItem -> equivSigs newItem priorItem && isVirt newItem && not (isVirt priorItem) @@ -594,7 +594,7 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this = items // Remove any F#-declared overrides. These may occur in the same type as the abstract member (unlike with .NET metadata) // Include any 'newslot' declared methods. - |> List.map filterDefiniteOverride + |> List.map filterDefiniteOverrides // Remove any virtuals that are signature-equivalent to virtuals in subtypes, except for newslots // That is, keep if it's From b41ce86aad637104d2f523c81de4b9b79819ab5a Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 16 Jan 2023 19:38:44 +0100 Subject: [PATCH 5/8] One more test --- .../ErrorMessages/ClassesTests.fs | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs index 490f3787644..fe3579c88ef 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs @@ -625,4 +625,33 @@ type Over () = |> withReferences [CSLib] |> withLangVersionPreview |> compile - |> shouldSucceed \ No newline at end of file + |> shouldSucceed + + + [] + let ``Disallow implementing more than one abstract slot`` () = + let app = FSharp """ +module ClassTests + +[] +type PA() = + abstract M : int -> unit + +[] +type PB<'a>() = + inherit PA() + abstract M : 'a -> unit +[] +type PC() = + inherit PB() + // Here, PA.M and PB.M have the same signature, so PA.M is unimplementable. + // REVIEW: in future we may give a friendly error at this point +type PD() = + inherit PC() + override this.M(x: int) = () + """ + app + |> withLangVersionPreview + |> compile + |> shouldFail + |> withSingleDiagnostic (Error 361, Line 19, Col 18, Line 19, Col 19, "The override 'M: int -> unit' implements more than one abstract slot, e.g. 'abstract PB.M: 'a -> unit' and 'abstract PA.M: int -> unit'") \ No newline at end of file From c8ad65979dbd39ab1d53a090d566b7e4ff959229 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 16 Jan 2023 19:39:14 +0100 Subject: [PATCH 6/8] One more test --- .../ErrorMessages/ClassesTests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs index fe3579c88ef..abd54c73308 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs @@ -654,4 +654,4 @@ type PD() = |> withLangVersionPreview |> compile |> shouldFail - |> withSingleDiagnostic (Error 361, Line 19, Col 18, Line 19, Col 19, "The override 'M: int -> unit' implements more than one abstract slot, e.g. 'abstract PB.M: 'a -> unit' and 'abstract PA.M: int -> unit'") \ No newline at end of file + |> withSingleDiagnostic (Error 361, Line 19, Col 19, Line 19, Col 20, "The override 'M: int -> unit' implements more than one abstract slot, e.g. 'abstract PB.M: 'a -> unit' and 'abstract PA.M: int -> unit'") \ No newline at end of file From 4a34a2d27771c0335b6d463c93690b36b2dd6384 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 17 Jan 2023 11:52:43 +0100 Subject: [PATCH 7/8] One more test --- .../ErrorMessages/ClassesTests.fs | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs index abd54c73308..52824cf7278 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs @@ -654,4 +654,36 @@ type PD() = |> withLangVersionPreview |> compile |> shouldFail - |> withSingleDiagnostic (Error 361, Line 19, Col 19, Line 19, Col 20, "The override 'M: int -> unit' implements more than one abstract slot, e.g. 'abstract PB.M: 'a -> unit' and 'abstract PA.M: int -> unit'") \ No newline at end of file + |> withSingleDiagnostic (Error 361, Line 19, Col 19, Line 19, Col 20, "The override 'M: int -> unit' implements more than one abstract slot, e.g. 'abstract PB.M: 'a -> unit' and 'abstract PA.M: int -> unit'") + + [] + let ``Generic overrides work with preview version`` () = + let CSLib = + CSharp """ +public class C +{ + public virtual void M(T1? a, T2 b, T1? c, T3? d) {} +} + +public class D : C +{ + public override void M(T1? a, T2 b, T1? c, T3? d) + where T1 : default + where T3 : default + { + base.M(a, b, c, d); + } +} + """ |> withName "CSLib" + + let app = + FSharp """ +module ClassTests +type X = + inherit C + override this.M(a, b, c, d) = () + """ |> withReferences [CSLib] + app + |> withLangVersionPreview + |> compile + |> shouldSucceed \ No newline at end of file From 8b9c7d6d171eebe763b338f4b5c08ab21d8e5065 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 19 Jan 2023 13:05:06 +0100 Subject: [PATCH 8/8] pr suggestion --- src/Compiler/Checking/InfoReader.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Checking/InfoReader.fs b/src/Compiler/Checking/InfoReader.fs index e17e566c750..5a6b63722cc 100644 --- a/src/Compiler/Checking/InfoReader.fs +++ b/src/Compiler/Checking/InfoReader.fs @@ -564,7 +564,7 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this = /// Filter the overrides of methods or properties, either keeping the overrides or keeping the dispatch slots. static let FilterOverrides findFlag (isVirt:'a->bool, isNewSlot, isDefiniteOverride, isFinal, equivSigs, nmf:'a->string) items = let equivVirts x y = isVirt x && isVirt y && equivSigs x y - let filterDefiniteOverrides = List.filter(not << isDefiniteOverride) + let filterDefiniteOverrides = List.filter(isDefiniteOverride >> not) match findFlag with | DiscardOnFirstNonOverride ->