Skip to content

Some checks don't be performed on F#-style extension methods #19227

@ijklam

Description

@ijklam

These checks don't be performed on F#-style extension methods.

for minfo in immediateMeths do
let nm = minfo.LogicalName
let m = (match minfo.ArbitraryValRef with None -> m | Some vref -> vref.DefinitionRange)
let others = getOtherMethods minfo
// abstract/default pairs of duplicate methods are OK
let IsAbstractDefaultPair (x: MethInfo) (y: MethInfo) =
x.IsDispatchSlot && y.IsDefiniteFSharpOverride
let IsAbstractDefaultPair2 (minfo: MethInfo) (minfo2: MethInfo) =
IsAbstractDefaultPair minfo minfo2 || IsAbstractDefaultPair minfo2 minfo
let checkForDup erasureFlag (minfo2: MethInfo) =
not (IsAbstractDefaultPair2 minfo minfo2)
&& (minfo.IsInstance = minfo2.IsInstance)
&& MethInfosEquivWrtUniqueness erasureFlag m minfo minfo2
if others |> List.exists (checkForDup EraseAll) then
if others |> List.exists (checkForDup EraseNone) then
errorR(Error(FSComp.SR.chkDuplicateMethod(nm, NicePrint.minimalStringOfType cenv.denv ty), m))
else
errorR(Error(FSComp.SR.chkDuplicateMethodWithSuffix(nm, NicePrint.minimalStringOfType cenv.denv ty), m))
let numCurriedArgSets = minfo.NumArgs.Length
if numCurriedArgSets > 1 && others |> List.exists (fun minfo2 -> not (IsAbstractDefaultPair2 minfo minfo2)) then
errorR(Error(FSComp.SR.chkDuplicateMethodCurried(nm, NicePrint.minimalStringOfType cenv.denv ty), m))
if numCurriedArgSets > 1 &&
(minfo.GetParamDatas(cenv.amap, m, minfo.FormalMethodInst)
|> List.existsSquared (fun (ParamData(isParamArrayArg, _isInArg, isOutArg, optArgInfo, callerInfo, _, reflArgInfo, ty)) ->
isParamArrayArg || isOutArg || reflArgInfo.AutoQuote || optArgInfo.IsOptional || callerInfo <> NoCallerInfo || isByrefLikeTy g m ty)) then
errorR(Error(FSComp.SR.chkCurriedMethodsCantHaveOutParams(), m))
if numCurriedArgSets = 1 then
let inline tryDestOptionalTy g ty =
if isOptionTy g ty then
destOptionTy g ty |> ValueSome
elif g.langVersion.SupportsFeature LanguageFeature.SupportValueOptionsAsOptionalParameters && isValueOptionTy g ty then
destValueOptionTy g ty |> ValueSome
else
ValueNone
let errorIfNotStringTy m ty callerInfo =
if not (typeEquiv g g.string_ty ty) then
errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, "string", NicePrint.minimalStringOfType cenv.denv ty), m))
let errorIfNotOptional tyToCompare desiredTyName m ty callerInfo =
match tryDestOptionalTy g ty with
| ValueSome t when typeEquiv g tyToCompare t -> ()
| ValueSome innerTy -> errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, desiredTyName, NicePrint.minimalStringOfType cenv.denv innerTy), m))
| ValueNone -> errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, desiredTyName, NicePrint.minimalStringOfType cenv.denv ty), m))
minfo.GetParamDatas(cenv.amap, m, minfo.FormalMethodInst)
|> List.iterSquared (fun (ParamData(_, isInArg, _, optArgInfo, callerInfo, nameOpt, _, ty)) ->
ignore isInArg
let m =
match nameOpt with
| Some name -> name.idRange
| None -> m
match (optArgInfo, callerInfo) with
| _, NoCallerInfo -> ()
| NotOptional, _ -> errorR(Error(FSComp.SR.tcCallerInfoNotOptional(callerInfo |> string), m))
| CallerSide _, CallerLineNumber ->
if not (typeEquiv g g.int32_ty ty) then
errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, "int", NicePrint.minimalStringOfType cenv.denv ty), m))
| CalleeSide, CallerLineNumber -> errorIfNotOptional g.int32_ty "int" m ty callerInfo
| CallerSide _, (CallerFilePath | CallerMemberName) -> errorIfNotStringTy m ty callerInfo
| CalleeSide, (CallerFilePath | CallerMemberName) -> errorIfNotOptional g.string_ty "string" m ty callerInfo
)

Repro steps

Put this code into fsi:

open System.Runtime.CompilerServices;;
  
[<Extension>]
type C =
    // FS0440: Methods with curried arguments cannot declare 'out', 'ParamArray', 'optional', 'ReflectedDefinition', 'byref', 'CallerLineNumber', 'CallerMemberName', or 'CallerFilePath' arguments
    [<Extension>]
    static member B(this: obj)(arg: string outref) = ()
    // FS1246: 'CallerLineNumber' must be applied to an argument of type 'int', but has been applied to an argument of type 'string'
    [<Extension>]
    static member C(this: obj, [<CallerLineNumber>] ?line: string) = ()
;;

type System.Object with
  // No error
  member this.B()(arg: string outref) = ()
  // No error
  member this.C([<CallerLineNumber>] ?line: string) = ()
;;

Expected behavior

Both C#-style and F#-style extension methods give the same error reports.

Actual behavior

Only C#-style extension methods gives the error reports.

Known workarounds

Not using F#-style extension methods.

Related information

  • Microsoft (R) F# Interactive version F# 10.0 的 14.0.101.0
  • .NET 10.0.101

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    Status

    New

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions