Skip to content

Commit

Permalink
More tuple tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv committed Aug 15, 2024
1 parent 817b5f0 commit fb78196
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 32 deletions.
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ void ensureAllUnderlyingConversionsChecked(SyntaxNode syntax, BoundExpression so
{
Debug.Assert(conversion.UnderlyingConversions.Length == 1);

if (destination.IsNullableType())
if (destination.IsNullableType()) // TODO2
{
switch (source.Type?.IsNullableType())
{
Expand Down Expand Up @@ -2242,7 +2242,7 @@ private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTup

NamedTypeSymbol targetType = (NamedTypeSymbol)destinationWithoutNullable;
// TODO2
if (destination.GetExtendedTypeNoUseSiteDiagnostics(null) is NamedTypeSymbol extendedType)
if (destinationWithoutNullable.GetExtendedTypeNoUseSiteDiagnostics(null) is NamedTypeSymbol extendedType)
{
targetType = extendedType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,35 +105,6 @@ public Conversion ClassifyImplicitConversionFromExpression(BoundExpression sourc

var sourceType = sourceExpression.Type;

// TODO2
//if (destination.GetExtendedTypeNoUseSiteDiagnostics(null) is { } extendedDestination)
//{
// if (sourceType is not null
// && sourceType.GetExtendedTypeNoUseSiteDiagnostics(null) is { } extendedSource2)
// {
// // TODO2 handle case where source is also an extension type
// if (HasIdentityConversionInternal(sourceType, destination))
// {
// return Conversion.Identity;
// }
// return Conversion.NoConversion;
// }

// var foundConversion = ClassifyImplicitConversionFromExpression(sourceExpression, extendedDestination, ref useSiteInfo);
// return foundConversion.Exists
// ? Conversion.CreateImplicitExtensionConversion(fromExtension: false, foundConversion, toExtension: true)
// : Conversion.NoConversion;
//}

//if (sourceType is not null
// && sourceType.GetExtendedTypeNoUseSiteDiagnostics(null) is { } extendedSource)
//{
// var foundConversion = ClassifyImplicitConversionFromType(extendedSource, destination, ref useSiteInfo);
// return foundConversion.Exists
// ? Conversion.CreateImplicitExtensionConversion(fromExtension: true, foundConversion, toExtension: false)
// : Conversion.NoConversion;
//}

//PERF: identity conversion is by far the most common implicit conversion, check for that first
if (sourceType is { } && HasIdentityConversionInternal(sourceType, destination))
{
Expand Down Expand Up @@ -2438,6 +2409,7 @@ private Conversion ClassifyImplicitNullableConversion(TypeSymbol source, TypeSym
return Conversion.ImplicitNullableWithImplicitNumericUnderlying;
}

// TODO2 test conversion to/from ENullableOfTuple
var tupleConversion = ClassifyImplicitTupleConversion(unwrappedSource, unwrappedDestination, ref useSiteInfo);
if (tupleConversion.Exists)
{
Expand Down Expand Up @@ -2580,6 +2552,16 @@ private Conversion ClassifyTupleConversion(
ImmutableArray<TypeWithAnnotations> sourceTypes;
ImmutableArray<TypeWithAnnotations> destTypes;

if (source.GetExtendedTypeNoUseSiteDiagnostics(null) is { } sourceExtendedType)
{
source = sourceExtendedType;
}

if (destination.GetExtendedTypeNoUseSiteDiagnostics(null) is { } destinationExtendedType)
{
destination = destinationExtendedType;
}

if (!source.TryGetElementTypesWithAnnotationsIfTupleType(out sourceTypes) ||
!destination.TryGetElementTypesWithAnnotationsIfTupleType(out destTypes) ||
sourceTypes.Length != destTypes.Length)
Expand Down
135 changes: 134 additions & 1 deletion src/Compilers/CSharp/Test/Emit3/ExtensionTypeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51955,7 +51955,6 @@ public void Conversion_ImplicitTuple_NestedImplicitConversions()
// Spec: An implicit conversion exists from a tuple expression E to a tuple type T if E has the same arity as T and
// an implicit conversion exists from each element in E to the corresponding element type in T.

// TODO2 broken
var src = """
E e = (1, "ran");
e.Print();
Expand All @@ -51975,6 +51974,140 @@ public void Conversion_ImplicitTuple_NestedImplicitConversions()
Assert.Equal(ConversionKind.ImplicitReference, conversion.UnderlyingConversions[1].Kind);
}

[Fact]
public void Conversion_ImplicitNullable_ImplicitTuple_FromNullableToNullable_ToExtension()
{
// Spec: For each of the predefined implicit or explicit conversions that convert
// from a non-nullable value type S to a non-nullable value type T, the following nullable conversions exist:

// TODO2 broken, resume here
// TODO2 There may be a spec issue (implicit tuple conversion should also cover expressions of tuple type)
// - An implicit or explicit conversion from S? to T?
var src = """
(int, string)? t = (1, "ran");
E? e = t;
e.Value.Print();

public explicit extension E for (long, object) { public void Print() { System.Console.Write(this); } }
""";

var comp = CreateCompilation([src, ExtensionErasureAttributeDefinition]);
CompileAndVerify(comp, expectedOutput: """(1, ran)""").VerifyDiagnostics();

var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var expr = GetSyntax<IdentifierNameSyntax>(tree, "t");
var conversion = model.GetConversion(expr);
Assert.Equal(ConversionKind.ImplicitNullable, conversion.Kind);
Assert.Equal(ConversionKind.ImplicitTupleLiteral, conversion.UnderlyingConversions[0].Kind);
Assert.Equal(ConversionKind.ImplicitNumeric, conversion.UnderlyingConversions[0].UnderlyingConversions[0].Kind);
Assert.Equal(ConversionKind.ImplicitReference, conversion.UnderlyingConversions[0].UnderlyingConversions[1].Kind);
}

[Fact]
public void Conversion_ImplicitNullable_ImplicitTuple_FromNullableToNullable_FromExtension()
{
// Spec: For each of the predefined implicit or explicit conversions that convert
// from a non-nullable value type S to a non-nullable value type T, the following nullable conversions exist:

// TODO2 broken
// TODO2 There may be a spec issue (implicit tuple conversion should also cover expressions of tuple type)
// - An implicit or explicit conversion from S? to T?
var src = """
E? t = (1, "ran");
(long, object)? e = t;
System.Console.Write(e.Value);

public explicit extension E for (int, string) { public void Print() { System.Console.Write(this); } }
""";

var comp = CreateCompilation([src, ExtensionErasureAttributeDefinition]);
CompileAndVerify(comp, expectedOutput: """(1, ran)""").VerifyDiagnostics();

var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var expr = GetSyntax<IdentifierNameSyntax>(tree, "t");
var conversion = model.GetConversion(expr);
Assert.Equal(ConversionKind.ImplicitNullable, conversion.Kind);
Assert.Equal(ConversionKind.ImplicitTupleLiteral, conversion.UnderlyingConversions[0].Kind);
Assert.Equal(ConversionKind.ImplicitNumeric, conversion.UnderlyingConversions[0].UnderlyingConversions[0].Kind);
Assert.Equal(ConversionKind.ImplicitReference, conversion.UnderlyingConversions[0].UnderlyingConversions[1].Kind);
}

[Fact]
public void Conversion_ImplicitNullable_ImplicitTuple_ToNullable()
{
// Spec: For each of the predefined implicit or explicit conversions that convert
// from a non-nullable value type S to a non-nullable value type T, the following nullable conversions exist:

// - An implicit or explicit conversion from S to T?
var src = """
E? e = (1, "ran");
e.Value.Print();

public explicit extension E for (long, object) { public void Print() { System.Console.Write(this); } }
""";

var comp = CreateCompilation([src, ExtensionErasureAttributeDefinition]);
CompileAndVerify(comp, expectedOutput: """(1, ran)""").VerifyDiagnostics();

var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var expr = GetSyntax<TupleExpressionSyntax>(tree, """(1, "ran")""");
var conversion = model.GetConversion(expr);
Assert.Equal(ConversionKind.ImplicitNullable, conversion.Kind);
Assert.Equal(ConversionKind.ImplicitTupleLiteral, conversion.UnderlyingConversions[0].Kind);
Assert.Equal(ConversionKind.ImplicitNumeric, conversion.UnderlyingConversions[0].UnderlyingConversions[0].Kind);
Assert.Equal(ConversionKind.ImplicitReference, conversion.UnderlyingConversions[0].UnderlyingConversions[1].Kind);
}

[Fact]
public void Conversion_ExplicitNullable_ImplicitTuple_FromNullable()
{
// Spec: For each of the predefined implicit or explicit conversions that convert
// from a non-nullable value type S to a non-nullable value type T, the following nullable conversions exist:

// - An explicit conversion from S? to T.
var src = """
(int, string)? t = (1, "ran");
E e = t;
e.Print();

public explicit extension E for (long, object) { public void Print() { System.Console.Write(this); } }
""";

var comp = CreateCompilation([src, ExtensionErasureAttributeDefinition]);
comp.VerifyEmitDiagnostics(
// (2,7): error CS0029: Cannot implicitly convert type '(int, string)?' to 'E'
// E e = t;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "t").WithArguments("(int, string)?", "E").WithLocation(2, 7));

var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var expr = GetSyntax<IdentifierNameSyntax>(tree, "t");
var conversion = model.GetConversion(expr);
//Assert.Equal(ConversionKind.ImplicitNullable, conversion.Kind);
//Assert.Equal(ConversionKind.ImplicitTupleLiteral, conversion.UnderlyingConversions[0].Kind);
//Assert.Equal(ConversionKind.ImplicitNumeric, conversion.UnderlyingConversions[0].UnderlyingConversions[0].Kind);
//Assert.Equal(ConversionKind.ImplicitReference, conversion.UnderlyingConversions[0].UnderlyingConversions[1].Kind);

// TODO2
src = """
(int, string)? t = (1, "ran");
E e = (E)t;
e.Print();

public explicit extension E for (long, object) { public void Print() { System.Console.Write(this); } }
""";

comp = CreateCompilation([src, ExtensionErasureAttributeDefinition]);
comp.VerifyEmitDiagnostics(
// (2,7): error CS0029: Cannot implicitly convert type '(int, string)?' to 'E'
// E e = t;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "t").WithArguments("(int, string)?", "E").WithLocation(2, 7));
// TODO2 execute
}

[Fact]
public void Conversion_HasImplicitEnumerationConversion()
{
Expand Down

0 comments on commit fb78196

Please sign in to comment.