未验证 提交 3e6a14a0 编写于 作者: R Rikki Gibson 提交者: GitHub

Don't allow void to convert to object via user-defined conversion (#35184)

* Add reproducer for #34872

* Fix bug and add test

* WIP

* Test similar scenario in VB

* Add void check in ClassifyConversionFromType, update tuple tests

* Cleanup
上级 672fed57
......@@ -168,6 +168,32 @@ public Conversion ClassifyConversionFromExpressionType(TypeSymbol source, TypeSy
return ClassifyConversionFromType(source, destination, ref useSiteDiagnostics);
}
private static bool TryGetVoidConversion(TypeSymbol source, TypeSymbol destination, out Conversion conversion)
{
var sourceIsVoid = source?.SpecialType == SpecialType.System_Void;
var destIsVoid = destination.SpecialType == SpecialType.System_Void;
// 'void' is not supposed to be able to convert to or from anything, but in practice,
// a lot of code depends on checking whether an expression of type 'void' is convertible to 'void'.
// (e.g. for an expression lambda which returns void).
// Therefore we allow an identity conversion between 'void' and 'void'.
if (sourceIsVoid && destIsVoid)
{
conversion = Conversion.Identity;
return true;
}
// If exactly one of source or destination is of type 'void' then no conversion may exist.
if (sourceIsVoid || destIsVoid)
{
conversion = Conversion.NoConversion;
return true;
}
conversion = default;
return false;
}
/// <summary>
/// Determines if the source expression is convertible to the destination type via
/// any conversion: implicit, explicit, user-defined or built-in.
......@@ -182,6 +208,11 @@ public Conversion ClassifyConversionFromExpression(BoundExpression sourceExpress
Debug.Assert(sourceExpression != null);
Debug.Assert((object)destination != null);
if (TryGetVoidConversion(sourceExpression.Type, destination, out var conversion))
{
return conversion;
}
if (forCast)
{
return ClassifyConversionFromExpressionForCast(sourceExpression, destination, ref useSiteDiagnostics);
......@@ -210,6 +241,11 @@ public Conversion ClassifyConversionFromType(TypeSymbol source, TypeSymbol desti
Debug.Assert((object)source != null);
Debug.Assert((object)destination != null);
if (TryGetVoidConversion(source, destination, out var voidConversion))
{
return voidConversion;
}
if (forCast)
{
return ClassifyConversionFromTypeForCast(source, destination, ref useSiteDiagnostics);
......
......@@ -6027,10 +6027,7 @@ static void Main()
Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(5, 17),
// (5,31): error CS8210: A tuple may not contain a value of type 'void'.
// (int x, void y) = (1, Main());
Diagnostic(ErrorCode.ERR_VoidInTuple, "Main()").WithLocation(5, 31),
// (5,17): error CS0029: Cannot implicitly convert type 'void' to 'void'
// (int x, void y) = (1, Main());
Diagnostic(ErrorCode.ERR_NoImplicitConv, "void y").WithArguments("void", "void").WithLocation(5, 17)
Diagnostic(ErrorCode.ERR_VoidInTuple, "Main()").WithLocation(5, 31)
);
var main = comp.GetMember<MethodSymbol>("C.Main");
var tree = comp.SyntaxTrees[0];
......@@ -6113,10 +6110,7 @@ static void Main()
Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(5, 17),
// (5,31): error CS0029: Cannot implicitly convert type 'int' to 'void'
// (int x, void y) = (1, 2);
Diagnostic(ErrorCode.ERR_NoImplicitConv, "2").WithArguments("int", "void").WithLocation(5, 31),
// (5,17): error CS0029: Cannot implicitly convert type 'void' to 'void'
// (int x, void y) = (1, 2);
Diagnostic(ErrorCode.ERR_NoImplicitConv, "void y").WithArguments("void", "void").WithLocation(5, 17)
Diagnostic(ErrorCode.ERR_NoImplicitConv, "2").WithArguments("int", "void").WithLocation(5, 31)
);
var tree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(tree);
......
......@@ -1597,5 +1597,49 @@ static void Main()
";
CreateEmptyCompilation(source).VerifyDiagnostics();
}
[Fact, WorkItem(34876, "https://github.com/dotnet/roslyn/pull/34876")]
public void GenericOperatorVoidConversion()
{
var source = @"
class C<T>
{
public static implicit operator C<T>(T t) => new C<T>();
private static void M1() { }
private static C<object> M2()
{
return M1();
}
}
";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (9,16): error CS0029: Cannot implicitly convert type 'void' to 'C<object>'
// return M1();
Diagnostic(ErrorCode.ERR_NoImplicitConv, "M1()").WithArguments("void", "C<object>").WithLocation(9, 16));
}
[Fact, WorkItem(34876, "https://github.com/dotnet/roslyn/pull/34876")]
public void GenericOperatorVoidConversion_Cast()
{
var source = @"
class C<T>
{
public static explicit operator C<T>(T t) => new C<T>();
private static void M1() { }
private static C<object> M2()
{
return (C<object>) M1();
}
}
";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (9,16): error CS0030: Cannot convert type 'void' to 'C<object>'
// return (C<object>) M1();
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C<object>) M1()").WithArguments("void", "C<object>").WithLocation(9, 16));
}
}
}
......@@ -1124,6 +1124,34 @@ BC30452: Operator '/' is not defined for types 'A14' and 'A14'.
</expected>)
End Sub
<Fact(), WorkItem(34872, "https://github.com/dotnet/roslyn/issues/34872")>
Public Sub GenericOperatorVoidConversion()
Dim compilation = CompilationUtils.CreateCompilationWithMscorlib40(
<compilation name="C">
<file name="a.vb"><![CDATA[
Public Class C(Of T)
Public Shared Widening Operator CType(t As T) As C(Of T)
Return New C(Of T)
End Operator
Public Sub M()
End Sub
Public Function M2() As C(Of Object)
Return M()
End Function
End Class
]]></file>
</compilation>)
CompilationUtils.AssertTheseDiagnostics(compilation,
<expected><![CDATA[
BC30491: Expression does not produce a value.
Return M()
~~~
]]></expected>)
End Sub
<Fact()>
Public Sub Operators8()
Dim compilation = CompilationUtils.CreateCompilationWithMscorlib40(
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册