提交 a25ee304 编写于 作者: N Neal Gafter

Allow ?. expression as value of async Task method even if type is unconstrained

Fixes #74
Closes #799
上级 c3ba4e02
......@@ -6307,10 +6307,11 @@ private BoundConditionalAccess BindConditionalAccessExpression(ConditionalAccess
// access cannot be a pointer
if ((!accessType.IsReferenceType && !accessType.IsValueType) || accessType.IsPointerType())
{
// Assume result type of the access is void when result value isn't used and cannot be made nullable.
// We are not doing this for types that can be made nullable to still allow expression evaluator to
// to get the value.
bool resultIsNotUsed = false;
// Result type of the access is void when result value cannot be made nullable.
// For improved diagnostics we detect the cases where the value will be used and produce a
// more specific (though not technically correct) diagnostic here:
// "Error CS0023: Operator '?' cannot be applied to operand of type 'T'"
bool resultIsUsed = true;
CSharpSyntaxNode parent = node.Parent;
if (parent != null)
......@@ -6318,37 +6319,35 @@ private BoundConditionalAccess BindConditionalAccessExpression(ConditionalAccess
switch (parent.Kind())
{
case SyntaxKind.ExpressionStatement:
resultIsNotUsed = ((ExpressionStatementSyntax)parent).Expression == node;
resultIsUsed = ((ExpressionStatementSyntax)parent).Expression != node;
break;
case SyntaxKind.SimpleLambdaExpression:
resultIsNotUsed = (((SimpleLambdaExpressionSyntax)parent).Body == node) && ContainingMethodOrLambdaReturnsVoid();
resultIsUsed = (((SimpleLambdaExpressionSyntax)parent).Body != node) || ContainingMethodOrLambdaRequiresValue();
break;
case SyntaxKind.ParenthesizedLambdaExpression:
resultIsNotUsed = (((ParenthesizedLambdaExpressionSyntax)parent).Body == node) && ContainingMethodOrLambdaReturnsVoid();
resultIsUsed = (((ParenthesizedLambdaExpressionSyntax)parent).Body != node) || ContainingMethodOrLambdaRequiresValue();
break;
case SyntaxKind.ArrowExpressionClause:
resultIsNotUsed = (((ArrowExpressionClauseSyntax)parent).Expression == node) && ContainingMethodOrLambdaReturnsVoid();
resultIsUsed = (((ArrowExpressionClauseSyntax)parent).Expression != node) || ContainingMethodOrLambdaRequiresValue();
break;
case SyntaxKind.ForStatement:
// Incrementors and Initializers doesn't have to produce a value
var loop = (ForStatementSyntax)parent;
resultIsNotUsed = loop.Incrementors.Contains(node) || loop.Initializers.Contains(node);
resultIsUsed = !loop.Incrementors.Contains(node) && !loop.Initializers.Contains(node);
break;
}
}
if (resultIsNotUsed)
{
accessType = GetSpecialType(SpecialType.System_Void, diagnostics, node);
}
else
if (resultIsUsed)
{
return GenerateBadConditionalAccessNodeError(node, receiver, access, diagnostics);
}
accessType = GetSpecialType(SpecialType.System_Void, diagnostics, node);
}
// if access has value type, the type of the conditional access is nullable of that
......@@ -6360,10 +6359,13 @@ private BoundConditionalAccess BindConditionalAccessExpression(ConditionalAccess
return new BoundConditionalAccess(node, receiver, access, accessType);
}
private bool ContainingMethodOrLambdaReturnsVoid()
private bool ContainingMethodOrLambdaRequiresValue()
{
Symbol containingMember = ContainingMemberOrLambda;
return (object)containingMember != null && containingMember.Kind == SymbolKind.Method && ((MethodSymbol)containingMember).ReturnsVoid;
var containingMethod = ContainingMemberOrLambda as MethodSymbol;
return
(object)containingMethod == null ||
!containingMethod.ReturnsVoid &&
!containingMethod.IsTaskReturningAsync(this.Compilation);
}
private BoundConditionalAccess GenerateBadConditionalAccessNodeError(ConditionalAccessExpressionSyntax node, BoundExpression receiver, BoundExpression access, DiagnosticBag diagnostics)
......
......@@ -5542,5 +5542,57 @@ public T M()
M
---");
}
[WorkItem(74, "https://github.com/dotnet/roslyn/issues/74")]
[Fact]
public void ConditionalInAsyncTask()
{
var source = @"
#pragma warning disable CS1998 // suppress 'no await in async' warning
using System;
using System.Threading.Tasks;
class Foo<T>
{
public T Method(int i)
{
Console.Write(i);
return default(T); // returns value of unconstrained type parameter type
}
public void M1(Foo<T> x) => x?.Method(4);
public async void M2(Foo<T> x) => x?.Method(5);
public async Task M3(Foo<T> x) => x?.Method(6);
public async Task M4() {
Foo<T> a = new Foo<T>();
Foo<T> b = null;
Action f1 = async () => a?.Method(1);
f1();
f1 = async () => b?.Method(0);
f1();
Func<Task> f2 = async () => a?.Method(2);
await f2();
Func<Task> f3 = async () => b?.Method(3);
await f3();
M1(a); M1(b);
M2(a); M2(b);
await M3(a);
await M3(b);
}
}
class Program
{
public static void Main()
{
// this will complete synchronously as there are no truly async ops.
new Foo<int>().M4();
}
}";
var compilation = CreateCompilationWithMscorlib45(
source, references: new[] { SystemRef_v4_0_30319_17929, SystemCoreRef_v4_0_30319_17929, CSharpRef }, options: TestOptions.DebugExe);
CompileAndVerify(compilation, expectedOutput: "12456");
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册