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

Start proposal for more constant types #8257

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

BillWagner
Copy link
Member

First draft at a proposal to enable more types to be declared as const types.

@HaloFour
Copy link
Contributor

Attribute parameters

This proposal includes changing the runtime to understand and support for more types in attribute parameters? Would that also include the pseudoconstant decimal?

@KathleenDollard
Copy link
Collaborator

I love it!

## Summary
[summary]: #summary

Expand the allowable types for compile-time constants (`const` declarations) to any value type that is fully blittable.
Copy link
Member

Choose a reason for hiding this comment

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

C# notably doesn't have a concept of blittable, only unmanaged. blittable has a specific meaning, as it pertains to interop, and types like bool aren't considered blittable unless the user opts-in via DisableRuntimeMarshalling

@@ -0,0 +1,79 @@
# Unmanaged constant types
Copy link
Member

Choose a reason for hiding this comment

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

I have a similar proposal, from 2017, that goes over a different approach to supporting this functionality here: #688


The current specification [§12.23](https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#1223-constant-expressions) limits constant expressions to an enumerated set of types, or default reference expressions (`default`, or `null`).

This proposal expands the types allowed. The allowed types should include any unmanaged type ([§8.8](https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/types.md#88-unmanaged-types))
Copy link
Member

Choose a reason for hiding this comment

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

There's potential issues with regards to variable length types such as nint or nuint where today the underlying primitive constants are restricted to their 32-bit domain

public object SomeReference;
}
```

Copy link
Contributor

Choose a reason for hiding this comment

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

How do we reconcile this with the runtime concept of field constant?

Currently C# constants match the runtime's version of field constants. For example, a field can be declared that is a constant which is different than a static field with a value. The constant value is encoded in the metadata which the compiler uses to emit as load instructions at the use site instead of referencing the field at runtime, because no field exists for it at runtime. The kinds of values that can be field constants is constrained to those that can be encoded in metadata. I'm not sure what those are, but it probably does not include arbitrary types even if they are blittable.

Of course, C# could deviate from the runtime and allow other types to be constant but would have to change to use static fields for these instead of field constants.

Copy link
Member

@333fred 333fred Jul 3, 2024

Choose a reason for hiding this comment

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

C# already diverges from the runtime's concept of constants. Consider decimal:

const decimal d = 1.0M;

Results in:

    // Fields
    .field public static initonly valuetype [System.Runtime]System.Decimal d
    .custom instance void [System.Runtime]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(uint8, uint8, uint32, uint32, uint32) = (
        01 00 01 00 00 00 00 00 00 00 00 00 0a 00 00 00
        00 00
    )

Plus a static ctor to assign that value.

- **What types should be allowed?**: Preventing mutation for types whose members are all unmanaged types (or `struct` types whose members are all unmanaged types) is a manageable problem: a `const` instance can't be accessed as a writable `ref`. However, if any member is a reference type, that problem is harder to solve.
- **Are there implications for the runtime?** Depending on how constants are stored in memory, is there an impact on the runtime?
- **What are the implications of allowing constant ref structs? Are they even useful?**: Are the rules surrounding ref safety consistent with where constants are used?
- **How will down-level scenarios work?** Can these constants be accessed by code using previous compilers? What about using Reflection to access fields?
Copy link
Member

Choose a reason for hiding this comment

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

Yes, we should make sure that reflection handles these new constant types. Our users expect reflection to return C# constants as properly typed values (dotnet/runtime#104270 (comment) has some discussion about issues we have in this area currently.)

## Motivation
[motivation]: #motivation

Only constant expressions can be used in pattern matching expressions. This expands the types that can be used in patterns. For example, it's common to use GUIDs as identifiers. This feature enables matching on GUID values. Currently, the only way to do that is to convert a GUID to a ReadOnlySpan<byte> and use a list pattern on the list of bytes.
Copy link
Member

Choose a reason for hiding this comment

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

Expanding the notion of const in the language is a decent sized feature as it ends up touching on a number of areas. If the main motivation is to expand the set of types usable in patterns should we just approach that more directly?

Comment on lines +57 to +74
- **Does a type need to "opt in" to all constant definitions?** (Otherwise, changes later could introduce a field that disallows all existing `const` declarations):

```csharp
public struct Point
{
public int X;
public int Y;
}

// V2:

public struct Point
{
public int X;
public int Y;
public object SomeReference;
}
```

Choose a reason for hiding this comment

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

I don't think it should be opt in.
Perhaps if a struct could be explicitly defined as an unmanaged struct the user would think twice about introducing additional fields that are not also unmanaged.

   public unmanaged struct Point
   {
       public int X;
       public int Y;
   }

Related:
#2036
#1495
#688

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.

9 participants