-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
C# 13: Allows ref struct. #18385
base: main
Are you sure you want to change the base?
C# 13: Allows ref struct. #18385
Changes from 14 commits
c0974f3
958d8f1
d9158c8
41dc4a5
9a2edc3
ef9f09e
b9fce5e
ef5ae3f
33939a8
c439beb
5ddc378
cac1e04
ff32a38
caaf291
b358f33
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
--- | ||
category: minorAnalysis | ||
--- | ||
* C# 13: Added extractor support and call dispatch logic (data flow) for the (negative) type parameter constraint `allows ref struct`. Added extractor support for the type parameter constraint `notnull`. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,6 +48,11 @@ class Type extends Member, TypeContainer, @type { | |
|
||
/** Holds if this type is a value type, or a type parameter that is a value type. */ | ||
predicate isValueType() { none() } | ||
|
||
/** | ||
* Holds if this type is a ref like type. | ||
*/ | ||
predicate isRefLikeType() { none() } | ||
} | ||
|
||
pragma[nomagic] | ||
|
@@ -704,15 +709,38 @@ class Enum extends ValueType, @enum_type { | |
* ``` | ||
*/ | ||
class Struct extends ValueType, @struct_type { | ||
/** Holds if this `struct` has a `ref` modifier. */ | ||
predicate isRef() { this.hasModifier("ref") } | ||
/** | ||
* DEPRECATED: Use `instanceof RefStruct` instead. | ||
* | ||
* Holds if this `struct` has a `ref` modifier. | ||
*/ | ||
deprecated predicate isRef() { this.hasModifier("ref") } | ||
|
||
/** Holds if this `struct` has a `readonly` modifier. */ | ||
predicate isReadonly() { this.hasModifier("readonly") } | ||
|
||
override string getAPrimaryQlClass() { result = "Struct" } | ||
} | ||
|
||
/** | ||
* A `ref struct`, for example | ||
* | ||
* ```csharp | ||
* ref struct S { | ||
* ... | ||
* } | ||
* ``` | ||
*/ | ||
class RefStruct extends Struct { | ||
RefStruct() { this.hasModifier("ref") } | ||
|
||
override string getAPrimaryQlClass() { result = "RefStruct" } | ||
|
||
override predicate isValueType() { none() } | ||
|
||
override predicate isRefLikeType() { any() } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the same be added to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The intention is that this should only cover types (for now there only exists There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense. I guess that is also the behavior of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think so (it is not specifically well documented) - classes and interfaces have |
||
} | ||
|
||
/** | ||
* A `record struct`, for example | ||
* ```csharp | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,5 @@ | ||
import csharp | ||
|
||
from Struct s | ||
where | ||
s.fromSource() and | ||
s.isRef() | ||
from RefStruct s | ||
where s.fromSource() | ||
select s |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
public class TestClass | ||
{ | ||
public void M1<T1>(T1 x) where T1 : class { } | ||
|
||
public void M2<T2>(T2 x) where T2 : struct { } | ||
|
||
public void M3<T3>(T3 x) where T3 : unmanaged { } | ||
|
||
public void M4<T4>(T4 x) where T4 : new() { } | ||
|
||
public void M5<T5>(T5 x) where T5 : notnull { } | ||
|
||
public void M6<T6>(T6 x) where T6 : IList<object> { } | ||
|
||
public void M7<T7>(T7 x) where T7 : allows ref struct { } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
typeParameterContraints | ||
| TypeParameterConstraints.cs:6:20:6:21 | T1 | file://:0:0:0:0 | where T1: ... | | ||
| TypeParameterConstraints.cs:8:20:8:21 | T2 | file://:0:0:0:0 | where T2: ... | | ||
| TypeParameterConstraints.cs:10:20:10:21 | T3 | file://:0:0:0:0 | where T3: ... | | ||
| TypeParameterConstraints.cs:12:20:12:21 | T4 | file://:0:0:0:0 | where T4: ... | | ||
| TypeParameterConstraints.cs:14:20:14:21 | T5 | file://:0:0:0:0 | where T5: ... | | ||
| TypeParameterConstraints.cs:16:20:16:21 | T6 | file://:0:0:0:0 | where T6: ... | | ||
| TypeParameterConstraints.cs:18:20:18:21 | T7 | file://:0:0:0:0 | where T7: ... | | ||
specificParameterConstraints | ||
| TypeParameterConstraints.cs:16:20:16:21 | T6 | IList<object> | | ||
hasConstructorConstraint | ||
| TypeParameterConstraints.cs:12:20:12:21 | T4 | file://:0:0:0:0 | where T4: ... | | ||
hasRefTypeConstraint | ||
| TypeParameterConstraints.cs:6:20:6:21 | T1 | file://:0:0:0:0 | where T1: ... | | ||
hasValueTypeConstraint | ||
| TypeParameterConstraints.cs:8:20:8:21 | T2 | file://:0:0:0:0 | where T2: ... | | ||
| TypeParameterConstraints.cs:10:20:10:21 | T3 | file://:0:0:0:0 | where T3: ... | | ||
hasUnmanagedTypeConstraint | ||
| TypeParameterConstraints.cs:10:20:10:21 | T3 | file://:0:0:0:0 | where T3: ... | | ||
hasNullableRefTypeConstraint | ||
hasNotNullConstraint | ||
| TypeParameterConstraints.cs:14:20:14:21 | T5 | file://:0:0:0:0 | where T5: ... | | ||
hasAllowRefLikeTypeConstraint | ||
| TypeParameterConstraints.cs:18:20:18:21 | T7 | file://:0:0:0:0 | where T7: ... | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't we consider this a value type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is definitely up for debate.
As you write, the
ref struct
still exhibits call-by-value semantics. However, we also decided a while back that we needed to introduce post update nodes for ref structs parameters (asref structs
can haveref
fields - and in this case only the reference is copied, which from a dataflow perspective means that we "can" update a ref struct (or at least update the content of the shared references)). Furthermore,ref structs
can't be boxed (implicitly converted) toValueType
(orobject
). My thinking is that it is "something in between" and "reference like" (this is also the terminology used in Roslyn). So I thought that it was starting to get a bit dangerous to assume that it behaves like a value type both from a type and data flow perspective.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To continue the discussion - I don't mind that we still consider the
ref struct
a value type - then we can modify type conversion, unification and dataflow on a case by case when we run into problems.Maybe that is the safer choice.