Skip to content
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

Fix for issue 8351 #8354

Merged
merged 17 commits into from
Jun 23, 2020
Merged

Conversation

smoothdeveloper
Copy link
Contributor

tentative fix for #8351, not sure of the implication, picking the most visible of both getter and setter when both are defined in AccessibilityLogic.fs, otherwise same logic as before when only either is defined.

@smoothdeveloper smoothdeveloper changed the title Fix for issue 8351 [wip] Fix for issue 8351 Jan 25, 2020
let getA = (resolveILMethodRef tdef mrefGet).Access
let setA = (resolveILMethodRef tdef mrefSet).Access
// pick most accessible
max getA setA
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please let me know if using max here is problematic and how you'd work around it.

@cartermp
Copy link
Contributor

cartermp commented Jun 8, 2020

@KevinRansom @dsyme can you take a look?

@smoothdeveloper smoothdeveloper changed the title [wip] Fix for issue 8351 Fix for issue 8351 Jun 8, 2020
@KevinRansom
Copy link
Member

tentative fix for #8351, not sure of the implication, picking the most visible of both getter and setter when both are defined in AccessibilityLogic.fs, otherwise same logic as before when only either is defined.

Sigh!!! whilst this seems like a hack, if we look at how this function is used in the compiler, we can find this comment:

Here // For IL properties, we get an approximate accessibility that at least reports "internal" as "internal" and "private" as "private"

You have testing that shows, that later on we fix up the approximate nature of this resolution, and correctly fail when the setter is not visible here:(https://github.com/dotnet/fsharp/pull/8354/files#diff-ae28350224587d202ca4111c1125f62a)

Whilst I hate using the ordinal position of visibility in that list, it's probably the simplest and least error prone mechanism available. I do however reserve the right to moan about it continuously

@cartermp
Copy link
Contributor

cartermp commented Jun 9, 2020

I do however reserve the right to moan about it continuously
image

@KevinRansom
Copy link
Member

My role model ... right there

@cartermp
Copy link
Contributor

Yay, tests pass! @KevinRansom further thoughts?

Copy link
Member

@KevinRansom KevinRansom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a mechanism to determine visibility based on the "broadest visible item, is a good solution. I wish we didn't encode visibility broadness based on the ordinal position in the ILMemberAccess enum, however, this approach does accurately compute visibility.

Thanks for taking care of this

src/absil/il.fsi Outdated
@@ -727,16 +727,17 @@ type ILMethodBody =
SourceMarker: ILSourceMarker option }

/// Member Access
// important: order of definition matters for AccessibilityLogic.GetILAccessOfILPropInfo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to avoid having to rely on the order of this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TIHan , I hate it too, however, every time I tried to reproduce the actual broadening algorithm, it ended up being horrific. One thing I think we could do, is to assign a specific value to each enum value, that way it is specific and deterministic, and not determined by the order in the source file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But relying on the order of declarations is how we F#. 😄

Copy link
Contributor

@TIHan TIHan Jun 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KevinRansom I think we can do this without having to do some broadening algorithm.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lol you are not wrong ... but some developer who is not familiar with the ordering, might prefer to alphabetize the enum and break everything.
Arguably that is covered, by a comment that says, "the order is important".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we cover it with a test instead of a comment (or maybe choose a testing approach that makes comments actually useful by verifying their correctness with regard to the code)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonsequitur I've just added such test in tests/fsharp/Compiler/ILMemberAccessTests.fs : https://github.com/dotnet/fsharp/pull/8354/files#diff-6415f4a32cd76160025147135e46baaa

If cases are added/removed, the test, if not updated, will fail, and if some of the existing comparisons don't hold true anymore, it will also fail.

Please let me know if that fits the bill.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Roslyn also does ordering: http://sourceroslyn.io/#Microsoft.CodeAnalysis/Symbols/Accessibility.cs , http://sourceroslyn.io/#Microsoft.CodeAnalysis.CSharp/Symbols/PropertyOrEventSymbolExtensions.cs,b31562460f5b74cc,references

I think for us, we technically don't need it for correctness in the compiler because the compiler simply asks a question if it is visible or not - all we need to do is ask if the get or set method is visible. However, the tooling, such as Symbols.fs, does need to know which is more accessible in terms of ILMemberAccess.

If we are going to do ordering, let's make the order just like Roslyn:

[<RequireQualifiedAccess>]
type ILMemberAccess =
    | CompilerControlled
    | Private
    | FamilyAndAssembly // protected and internal
    | Family // protected
    | Assembly // internal
    | FamilyOrAssembly // protected or internal
    | Public

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TIHan I've updated it to match your order, thanks!

| FSProp (_, _, None, Some vref) -> IsValAccessible ad vref
| FSProp (_, _, Some vrefGet, Some vrefSet) ->
// pick most accessible
IsValAccessible ad vrefGet || IsValAccessible ad vrefSet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this makes a lot of sense. I feel like we need to do something similar on the ILProp side, maybe inside IsILPropInfoAccessible?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TIHan I think this is handled:

| Some mrefGet, Some mrefSet ->
let getA = (resolveILMethodRef tdef mrefGet).Access
let setA = (resolveILMethodRef tdef mrefSet).Access
// pick most accessible
max getA setA
| None, None -> ILMemberAccess.Public
ilAccess
let IsILPropInfoAccessible g amap m ad pinfo =
let ilAccess = GetILAccessOfILPropInfo pinfo
IsILTypeAndMemberAccessible g amap m ad ad pinfo.ILTypeInfo ilAccess

it returns the max accessible in GetILAccessOfILPropInfo which is then checked against IsILTypeAndMemberAccessible.

Does that make sense?

@KevinRansom
Copy link
Member

@smoothdeveloper, i have an alternative way of doing the comparison that doesn't rely on the specification order of ILMemberAccess du.

I'm taking a crack at tests that use the compiler instead of ILMemberAccessTests, to verify the visibility calculation which is kind of a complicated way of verifying that ILMember orders don't change :-)

Anyway don't hate on me for this, if you can think of a better alternative or reasons why this won't worl I would welcome the feedback.

let GetILAccessOfILPropInfo (ILPropInfo(tinfo, pdef)) =
    let tdef = tinfo.RawMetadata
    let ilAccess =
        match pdef.GetMethod, pdef.SetMethod with 
        | Some mref, None 
        | None, Some mref -> (resolveILMethodRef tdef mref).Access

        | Some mrefGet, Some mrefSet ->
            let getA = (resolveILMethodRef tdef mrefGet).Access
            let setA = (resolveILMethodRef tdef mrefSet).Access

            // use the accessors to determine the visibility of the property
            match getA, setA with
            | ILMemberAccess.Public, _
            | _, ILMemberAccess.Public -> ILMemberAccess.Public

            | ILMemberAccess.FamilyOrAssembly, _
            | _, ILMemberAccess.FamilyOrAssembly -> ILMemberAccess.FamilyOrAssembly

            | ILMemberAccess.Assembly, _
            | _, ILMemberAccess.Assembly -> ILMemberAccess.Assembly

            | ILMemberAccess.Family, _
            | _, ILMemberAccess.Family -> ILMemberAccess.Family

            | ILMemberAccess.FamilyAndAssembly, _
            | _, ILMemberAccess.FamilyAndAssembly -> ILMemberAccess.FamilyAndAssembly

            | _ -> ILMemberAccess.Private

        | None, None -> ILMemberAccess.Public

    ilAccess

@smoothdeveloper
Copy link
Contributor Author

@KevinRansom,

The drawbacks I see:

  • changing the order of the match cases may break the behaviour
  • the order isn't defined in the DU itself (I assume we'd move the comment I've initially put there and minimize the diff by reverting to the shuffled order)
  • more boiler plate code in the compiler versus in a test
  • the invariant is now expressed only in implementation detail and can't turn the build red

Nonetheless, I'm ok to go with the consensus in the team about what is the best approach, just let me know for me to make the adjustments.

@KevinRansom
Copy link
Member

@KevinRansom,

The drawbacks I see:

  • changing the order of the match cases may break the behaviour
  • the order isn't defined in the DU itself (I assume we'd move the comment I've initially put there and minimize the diff by reverting to the shuffled order)
  • more boiler plate code in the compiler versus in a test
  • the invariant is now expressed only in implementation detail and can't turn the build red

Nonetheless, I'm ok to go with the consensus in the team about what is the best approach, just let me know for me to make the adjustments.

@smoothdeveloper, I don't like it either and I'm wondering if a comparer might be a better. Honestly, it's annoying that the compiler needs a notion of property visibility rather than relying on the visibility of the method implementing the operation being performed. However, it is there, whats super fun, is this is only an approximation, because of the disconnect between F# visibilities and the CLR notion of visibilities.

All that said, at least the implementation detail this is dependent upon is the function responsible for doing this stuff.

Anyway, I'm going to look at it some more. I'm not confident that I love either solution yet.

@KevinRansom
Copy link
Member

@smoothdeveloper please take a look at this PR: #9445 it contains additional testing. It no longer is dependent on DU ordering. But will perform a lot worse.

@cartermp
Copy link
Contributor

@KevinRansom conflicts :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants