Skip to content

Commit c68b55b

Browse files
authored
Merge pull request #18385 from michaelnebel/csharp/allowsrefstruct
C# 13: Allows ref struct.
2 parents ca28087 + d0d5e0d commit c68b55b

File tree

20 files changed

+256
-25
lines changed

20 files changed

+256
-25
lines changed

csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameterConstraints.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ public override void Populate(TextWriter trapFile)
4040
if (Symbol.ReferenceTypeConstraintNullableAnnotation == NullableAnnotation.Annotated)
4141
trapFile.general_type_parameter_constraints(this, 5);
4242

43+
if (Symbol.HasNotNullConstraint)
44+
trapFile.general_type_parameter_constraints(this, 6);
45+
46+
if (Symbol.AllowsRefLikeType)
47+
trapFile.general_type_parameter_constraints(this, 7);
48+
4349
foreach (var abase in Symbol.GetAnnotatedTypeConstraints())
4450
{
4551
var t = Type.Create(Context, abase.Symbol);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* 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`.

csharp/ql/lib/semmle/code/csharp/Conversion.qll

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -649,11 +649,14 @@ predicate convBoxing(Type fromType, Type toType) {
649649
}
650650

651651
private predicate convBoxingValueType(ValueType fromType, Type toType) {
652-
toType instanceof ObjectType
653-
or
654-
toType instanceof DynamicType
655-
or
656-
toType instanceof SystemValueTypeClass
652+
(
653+
toType instanceof ObjectType
654+
or
655+
toType instanceof DynamicType
656+
or
657+
toType instanceof SystemValueTypeClass
658+
) and
659+
not fromType.isRefLikeType()
657660
or
658661
toType = fromType.getABaseInterface+()
659662
}

csharp/ql/lib/semmle/code/csharp/Generics.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ class TypeParameterConstraints extends Element, @type_parameter_constraints {
287287
/** Holds if these constraints include a nullable reference type constraint. */
288288
predicate hasNullableRefTypeConstraint() { general_type_parameter_constraints(this, 5) }
289289

290+
/** Holds if these constraints include a notnull type constraint. */
291+
predicate hasNotNullTypeConstraint() { general_type_parameter_constraints(this, 6) }
292+
293+
/** Holds if these constraints include a `allows ref struct` constraint. */
294+
predicate hasAllowRefLikeTypeConstraint() { general_type_parameter_constraints(this, 7) }
295+
290296
/** Gets a textual representation of these constraints. */
291297
override string toString() { result = "where " + this.getTypeParameter().getName() + ": ..." }
292298

csharp/ql/lib/semmle/code/csharp/Type.qll

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ class Type extends Member, TypeContainer, @type {
4848

4949
/** Holds if this type is a value type, or a type parameter that is a value type. */
5050
predicate isValueType() { none() }
51+
52+
/**
53+
* Holds if this type is a ref like type.
54+
*
55+
* Only `ref struct` types are considered ref like types.
56+
*/
57+
predicate isRefLikeType() { none() }
5158
}
5259

5360
pragma[nomagic]
@@ -704,15 +711,36 @@ class Enum extends ValueType, @enum_type {
704711
* ```
705712
*/
706713
class Struct extends ValueType, @struct_type {
707-
/** Holds if this `struct` has a `ref` modifier. */
708-
predicate isRef() { this.hasModifier("ref") }
714+
/**
715+
* DEPRECATED: Use `instanceof RefStruct` instead.
716+
*
717+
* Holds if this `struct` has a `ref` modifier.
718+
*/
719+
deprecated predicate isRef() { this.hasModifier("ref") }
709720

710721
/** Holds if this `struct` has a `readonly` modifier. */
711722
predicate isReadonly() { this.hasModifier("readonly") }
712723

713724
override string getAPrimaryQlClass() { result = "Struct" }
714725
}
715726

727+
/**
728+
* A `ref struct`, for example
729+
*
730+
* ```csharp
731+
* ref struct S {
732+
* ...
733+
* }
734+
* ```
735+
*/
736+
class RefStruct extends Struct {
737+
RefStruct() { this.hasModifier("ref") }
738+
739+
override string getAPrimaryQlClass() { result = "RefStruct" }
740+
741+
override predicate isRefLikeType() { any() }
742+
}
743+
716744
/**
717745
* A `record struct`, for example
718746
* ```csharp

csharp/ql/lib/semmle/code/csharp/Unification.qll

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -522,16 +522,21 @@ module Gvn {
522522

523523
/** Provides definitions related to type unification. */
524524
module Unification {
525-
/** A type parameter that is compatible with any type. */
525+
/** A type parameter that is compatible with any type except `ref struct`. */
526526
class UnconstrainedTypeParameter extends TypeParameter {
527-
UnconstrainedTypeParameter() { not exists(getATypeConstraint(this)) }
527+
UnconstrainedTypeParameter() {
528+
not exists(getATypeConstraint(this)) and not exists(getANegativeTypeConstraint(this))
529+
}
528530
}
529531

530532
/** A type parameter that is constrained. */
531533
class ConstrainedTypeParameter extends TypeParameter {
532534
int constraintCount;
533535

534-
ConstrainedTypeParameter() { constraintCount = strictcount(getATypeConstraint(this)) }
536+
ConstrainedTypeParameter() {
537+
constraintCount = count(getATypeConstraint(this)) + count(getANegativeTypeConstraint(this)) and
538+
constraintCount > 0
539+
}
535540

536541
/**
537542
* Holds if this type parameter is unifiable with type `t`.
@@ -559,29 +564,31 @@ module Unification {
559564
bindingset[this]
560565
pragma[inline_late]
561566
override predicate unifiable(Type t) {
562-
exists(TTypeParameterConstraint ttc | ttc = getATypeConstraint(this) |
567+
forall(TTypeParameterConstraint ttc | ttc = getATypeConstraint(this) |
563568
ttc = TRefTypeConstraint() and
564569
t.isRefType()
565570
or
566571
ttc = TValueTypeConstraint() and
567572
t.isValueType()
568573
or
569574
typeConstraintUnifiable(ttc, t)
570-
)
575+
) and
576+
(t.isRefLikeType() implies getANegativeTypeConstraint(this) = TAllowRefTypeConstraint())
571577
}
572578

573579
bindingset[this]
574580
pragma[inline_late]
575581
override predicate subsumes(Type t) {
576-
exists(TTypeParameterConstraint ttc | ttc = getATypeConstraint(this) |
582+
forall(TTypeParameterConstraint ttc | ttc = getATypeConstraint(this) |
577583
ttc = TRefTypeConstraint() and
578584
t.isRefType()
579585
or
580586
ttc = TValueTypeConstraint() and
581587
t.isValueType()
582588
or
583589
typeConstraintSubsumes(ttc, t)
584-
)
590+
) and
591+
(t.isRefLikeType() implies getANegativeTypeConstraint(this) = TAllowRefTypeConstraint())
585592
}
586593
}
587594

@@ -603,7 +610,8 @@ module Unification {
603610
t.isValueType()
604611
or
605612
typeConstraintUnifiable(ttc, t)
606-
)
613+
) and
614+
(t.isRefLikeType() implies getANegativeTypeConstraint(this) = TAllowRefTypeConstraint())
607615
}
608616

609617
bindingset[this]
@@ -617,7 +625,8 @@ module Unification {
617625
t.isValueType()
618626
or
619627
typeConstraintSubsumes(ttc, t)
620-
)
628+
) and
629+
(t.isRefLikeType() implies getANegativeTypeConstraint(this) = TAllowRefTypeConstraint())
621630
}
622631
}
623632

@@ -632,6 +641,9 @@ module Unification {
632641
not t instanceof TypeParameter
633642
}
634643

644+
cached
645+
newtype TTypeParameterNegativeConstraint = TAllowRefTypeConstraint()
646+
635647
cached
636648
TTypeParameterConstraint getATypeConstraint(TypeParameter tp) {
637649
exists(TypeParameterConstraints tpc | tpc = tp.getConstraints() |
@@ -650,6 +662,14 @@ module Unification {
650662
)
651663
}
652664

665+
cached
666+
TTypeParameterNegativeConstraint getANegativeTypeConstraint(TypeParameter tp) {
667+
exists(TypeParameterConstraints tpc | tpc = tp.getConstraints() |
668+
tpc.hasAllowRefLikeTypeConstraint() and
669+
result = TAllowRefTypeConstraint()
670+
)
671+
}
672+
653673
cached
654674
predicate typeConstraintUnifiable(TTypeConstraint ttc, Type t) {
655675
exists(Type t0 | ttc = TTypeConstraint(t0) | implicitConversionRestricted(t, t0))

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ module LocalFlow {
703703
or
704704
t = any(TypeParameter tp | not tp.isValueType())
705705
or
706-
t.(Struct).isRef()
706+
t.isRefLikeType()
707707
) and
708708
not exists(getALastEvalNode(result))
709709
}

csharp/ql/test/library-tests/conversion/boxing/Boxing.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,6 @@ void M()
4545
x1 = x15; // not a boxing conversion
4646
}
4747
}
48+
49+
// Ref structs can't be converted to a dynamic, object or valuetype.
50+
ref struct S { }

csharp/ql/test/library-tests/csharp11/PrintAst.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ RequiredMembers.cs:
847847
# 40| 0: [Parameter] value
848848
Scoped.cs:
849849
# 1| [Struct] S1
850-
# 2| [Struct] S2
850+
# 2| [RefStruct] S2
851851
# 7| [Class] ScopedModifierTest
852852
# 9| 5: [Method] M1
853853
# 9| -1: [TypeMention] int
@@ -1402,7 +1402,7 @@ Strings.cs:
14021402
Struct.cs:
14031403
# 1| [NamespaceDeclaration] namespace ... { ... }
14041404
# 3| 1: [Class] MyEmptyClass
1405-
# 5| 2: [Struct] RefStruct
1405+
# 5| 2: [RefStruct] RefStruct
14061406
# 7| 5: [Field] MyInt
14071407
# 7| -1: [TypeMention] int
14081408
# 8| 6: [Field] MyByte

csharp/ql/test/library-tests/csharp7.2/PrintAst.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ csharp72.cs:
3030
# 26| 0: [FieldAccess] access to field s
3131
# 29| 7: [DelegateType] Del
3232
# 32| [Struct] ReadonlyStruct
33-
# 36| [Struct] RefStruct
34-
# 40| [Struct] ReadonlyRefStruct
33+
# 36| [RefStruct] RefStruct
34+
# 40| [RefStruct] ReadonlyRefStruct
3535
# 44| [Class] NumericLiterals
3636
# 46| 5: [Field] binaryValue
3737
# 46| -1: [TypeMention] int

0 commit comments

Comments
 (0)