C#: Address review comments.

This commit is contained in:
Michael Nebel
2026-01-06 15:02:24 +01:00
parent 1817f9cdf7
commit b686890ba6
3 changed files with 43 additions and 31 deletions

View File

@@ -535,7 +535,7 @@ predicate convSpan(Type fromType, Type toType) {
|
convIdentity(fromElementType, toElementType)
or
convCovariance(fromElementType, toElementType)
convVariance(fromElementType, toElementType)
)
or
fromType instanceof SystemStringClass and
@@ -835,8 +835,8 @@ predicate convConversionOperator(Type fromType, Type toType) {
)
}
pragma[nomagic]
private predicate convVarianceAux(UnboundGenericType ugt, GenericType fromType, GenericType toType) {
/** 13.1.3.2: Variance conversion. */
private predicate convVariance(GenericType fromType, GenericType toType) {
// Semantically equivalent with
// ```ql
// ugt = fromType.getUnboundGeneric()
@@ -856,23 +856,10 @@ private predicate convVarianceAux(UnboundGenericType ugt, GenericType fromType,
// ```
// but performance is improved by explicitly evaluating the `i`th argument
// only when all preceding arguments are convertible.
Variance::convVarianceSingle(ugt, fromType, toType)
Variance::convVarianceSingle(_, fromType, toType)
or
Variance::convVarianceMultiple(ugt, fromType, toType, ugt.getNumberOfTypeParameters() - 1)
}
/** 13.1.3.2: Variance conversion. */
private predicate convVariance(GenericType fromType, GenericType toType) {
convVarianceAux(_, fromType, toType)
}
/**
* Holds, if `fromType` is covariance convertible to `toType`.
*/
private predicate convCovariance(GenericType fromType, GenericType toType) {
exists(UnboundGenericType ugt |
convVarianceAux(ugt, fromType, toType) and
forall(TypeParameter tp | tp = ugt.getATypeParameter() | tp.isOut())
Variance::convVarianceMultiple(ugt, fromType, toType, ugt.getNumberOfTypeParameters() - 1)
)
}

View File

@@ -3,9 +3,11 @@ using System.Collections.Generic;
public interface CovariantInterface<out T> { }
public interface ContravariantInterface<in T> { }
public interface InvariantInterface<T> { }
public interface Interface<out T1, T2> { }
public interface MixedInterface<out T1, in T2> { }
public class Base { }
@@ -16,14 +18,14 @@ public class C
public void M()
{
string[] stringArray = [];
string[][] stringArrayArray;
string[,] stringArray2D;
string[][] stringArrayArray = [];
string[,] stringArray2D = new string[0, 0];
Span<string> stringSpan = stringArray; // string[] -> Span<string>;
// Covariant conversions to ReadOnlySpan
// Assignments are included to illustrate that this compiles.
// Assignments are included to illustrate that it compiles.
// Only the use of the types matter in terms of test output.
// Covariant conversions to ReadOnlySpan
ReadOnlySpan<CovariantInterface<Base>> covariantInterfaceBaseReadOnlySpan;
ReadOnlySpan<CovariantInterface<Derived>> covariantInterfaceDerivedReadOnlySpan = default;
Span<CovariantInterface<Derived>> covariantInterfaceDerivedSpan = default;
@@ -37,18 +39,32 @@ public class C
stringReadOnlySpan = stringSpan; // Span<string> -> ReadOnlySpan<string>;
stringReadOnlySpan = stringArray; // string[] -> ReadOnlySpan<string>;
// Contravariant conversions to ReadOnlySpan
ReadOnlySpan<ContravariantInterface<Derived>> contravariantInterfaceDerivedReadOnlySpan;
ReadOnlySpan<ContravariantInterface<Base>> contravariantInterfaceBaseReadOnlySpan = default;
Span<ContravariantInterface<Base>> contravariantInterfaceBaseSpan = default;
ContravariantInterface<Base>[] contravariantInterfaceBaseArray = [];
contravariantInterfaceDerivedReadOnlySpan = contravariantInterfaceBaseReadOnlySpan; // ReadOnlySpan<ContravariantInterface<Base>> -> ReadOnlySpan<ContravariantInterface<Derived>>
contravariantInterfaceDerivedReadOnlySpan = contravariantInterfaceBaseSpan; // Span<ContravariantInterface<Base>> -> ReadOnlySpan<ContravariantInterface<Derived>>
contravariantInterfaceDerivedReadOnlySpan = contravariantInterfaceBaseArray; // ContravariantInterface<Base>[] -> ReadOnlySpan<ContravariantInterface<Derived>>
// Mixed variance conversions to ReadOnlySpan
ReadOnlySpan<MixedInterface<Base, Derived>> mixedInterfaceBaseReadOnlySpan;
ReadOnlySpan<MixedInterface<Derived, Base>> mixedInterfaceDerivedReadOnlySpan = default;
Span<MixedInterface<Derived, Base>> mixedInterfaceDerivedSpan = default;
MixedInterface<Derived, Base>[] mixedInterfaceDerivedArray = [];
mixedInterfaceBaseReadOnlySpan = mixedInterfaceDerivedReadOnlySpan; // ReadOnlySpan<MixedInterface<Derived, Base>> -> ReadOnlySpan<MixedInterface<Base, Derived>>
mixedInterfaceBaseReadOnlySpan = mixedInterfaceDerivedSpan; // Span<MixedInterface<Derived, Base>> -> ReadOnlySpan<MixedInterface<Base, Derived>>
mixedInterfaceBaseReadOnlySpan = mixedInterfaceDerivedArray; // MixedInterface<Derived, Base>[] -> ReadOnlySpan<MixedInterface<Base, Derived>>
// Convert string to ReadOnlySpan<char>
string s = "";
ReadOnlySpan<char> charReadOnlySpan = s; // string -> ReadOnlySpan<char>
// Use the non-covariant interfaces to show that no conversion is possible.
// No conversion possible except for identity.
ReadOnlySpan<InvariantInterface<Base>> invariantInterfaceBaseReadOnlySpan;
ReadOnlySpan<InvariantInterface<Derived>> invariantInterfaceDerivedReadOnlySpan;
Span<InvariantInterface<Derived>> invariantInterfaceDerivedSpan;
InvariantInterface<Derived>[] invariantInterfaceDerivedArray;
ReadOnlySpan<Interface<Base, string>> interfaceBaseReadOnlySpan;
ReadOnlySpan<Interface<Derived, string>> interfaceDerivedReadOnlySpan;
Span<Interface<Derived, string>> interfaceDerivedSpan;
Interface<Derived, string>[] interfaceDerivedArray;
}
}

View File

@@ -1,15 +1,24 @@
| ContravariantInterface<Base>[] | ReadOnlySpan<ContravariantInterface<Base>> |
| ContravariantInterface<Base>[] | ReadOnlySpan<ContravariantInterface<Derived>> |
| ContravariantInterface<Base>[] | Span<ContravariantInterface<Base>> |
| CovariantInterface<Derived>[] | ReadOnlySpan<CovariantInterface<Base>> |
| CovariantInterface<Derived>[] | ReadOnlySpan<CovariantInterface<Derived>> |
| CovariantInterface<Derived>[] | Span<CovariantInterface<Derived>> |
| Interface<Derived,String>[] | ReadOnlySpan<Interface<Derived, string>> |
| Interface<Derived,String>[] | Span<Interface<Derived, string>> |
| InvariantInterface<Derived>[] | ReadOnlySpan<InvariantInterface<Derived>> |
| InvariantInterface<Derived>[] | Span<InvariantInterface<Derived>> |
| MixedInterface<Derived,Base>[] | ReadOnlySpan<MixedInterface<Base, Derived>> |
| MixedInterface<Derived,Base>[] | ReadOnlySpan<MixedInterface<Derived, Base>> |
| MixedInterface<Derived,Base>[] | Span<MixedInterface<Derived, Base>> |
| ReadOnlySpan<ContravariantInterface<Base>> | ReadOnlySpan<ContravariantInterface<Derived>> |
| ReadOnlySpan<CovariantInterface<Derived>> | ReadOnlySpan<CovariantInterface<Base>> |
| ReadOnlySpan<MixedInterface<Derived, Base>> | ReadOnlySpan<MixedInterface<Base, Derived>> |
| Span<ContravariantInterface<Base>> | ReadOnlySpan<ContravariantInterface<Base>> |
| Span<ContravariantInterface<Base>> | ReadOnlySpan<ContravariantInterface<Derived>> |
| Span<CovariantInterface<Derived>> | ReadOnlySpan<CovariantInterface<Base>> |
| Span<CovariantInterface<Derived>> | ReadOnlySpan<CovariantInterface<Derived>> |
| Span<Interface<Derived, string>> | ReadOnlySpan<Interface<Derived, string>> |
| Span<InvariantInterface<Derived>> | ReadOnlySpan<InvariantInterface<Derived>> |
| Span<MixedInterface<Derived, Base>> | ReadOnlySpan<MixedInterface<Base, Derived>> |
| Span<MixedInterface<Derived, Base>> | ReadOnlySpan<MixedInterface<Derived, Base>> |
| Span<string> | ReadOnlySpan<string> |
| String[] | ReadOnlySpan<string> |
| String[] | Span<string> |