Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why class-level "unsafe" modifier not consistent when using "partial"?

Tags:

c#

unsafe

I have noticed a certain behavior when using unsafe modifier at the class-level on partial classes that I was hoping to get some clarification on.

I have been working on a rather large-wrapper, that for the sake of sanity, am splitting across multiple files using the partial modifier. The wrapper makes heavy use of unsafe pointers, therefore I opted to simply declare it on the class-level to cover everything within.

public static unsafe partial class Ruby
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static VALUE CLASS_OF(VALUE obj) => ((RBasic*) obj)->klass;
}

In another file:

public static unsafe partial class Ruby
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void* DATA_PTR(VALUE obj) => ((RData*) obj)->data;
}

The unsafe modifier is required for each partial declaration in order for the unsafe code to be "allowed" and compile, which is understandable, I would expect that the class declarations for partial classes would need to match exactly.

But using that logic, I am also allowed to have another file that is not unsafe:

[SuppressUnmanagedCodeSecurity]
public static partial class Ruby
{
    [DllImport(RUBY_LIBRARY, CallingConvention = CallingConvention.Cdecl)]
    public static extern VALUE rb_ivar_get(VALUE obj, ID name);
}

Here, I do NOT use the unsafe modifier, and it is perfectly acceptable (obviously no unsafe code in this file).

What I am was hoping to get clarification on why this allowed. Shouldn't the class declarations across each partial class match exactly? Changing/excluding any other class modifier is not permitted, such as private, public, abstract, etc., so why is this alright with unsafe. The behavior seems inconsistent in my opinion. My guess is that it operates on a different context by the compiler somehow, but this is just conjecture on my part, and was hoping someone more knowledgeable than I on this could shed some light.

like image 561
ForeverZer0 Avatar asked Aug 20 '18 21:08

ForeverZer0


1 Answers

Your assertion about modifiers requiring consistency is a bit off. For example, this is perfectly valid:

public abstract partial class Foo { }

partial class Foo { }

Foo will represent an abstract class, as one of it's parts is declared abstract. The rules on class modifiers for partial classes can be found in section 10.2.2 of the specification. Here's the text relevant to unsafe partial classes:

When the unsafe modifier is used on a partial type declaration, only that particular part is considered an unsafe context (§18.1).

When sealed or abstract are applied to a partial class, all parts of the class are considered sealed or abstract. It cannot, however, be both.

In fact, accessibility is the only class modifier on partials that must be consistent between all parts, but is not required to be set in all parts.

When a partial type declaration includes an accessibility specification (the public, protected, internal, and private modifiers) it must agree with all other parts that include an accessibility specification. If no part of a partial type includes an accessibility specification, the type is given the appropriate default accessibility (§3.5.1).

This essentially boils down to not being allowed to declare both a public partial class Bar and an internal partial class Bar, as the accessibility becomes inconsistent between parts. Additional partials that are declared without an accessibility will default to an accessibility that has been declared, or the default specified by the rules outlined in section 3.5.1 of the spec.

Note: The specification sections I have referenced are from the version of the specification included with Visual Studio 2017.

The ECMA-334 references are as follows:

23.2:

When the unsafe modifier is used on a partial type declaration (§15.2.7), only that particular part is considered an unsafe context.

15.2.2.1:

When a partial type declaration (§15.2.7) includes an accessibility specification (via the public, protected, internal, and private modifiers), that specification shall agree with all other parts that include an accessibility specification. If no part of a partial type includes an accessibility specification, the type is given the appropriate default accessibility (§8.5.2).

15.2.2.2 and 15.2.2.3 have the rules regarding the abstract and sealed modifiers.

like image 84
Jonathon Chase Avatar answered Oct 19 '22 07:10

Jonathon Chase