Async CTP Refresh - what bugs remain in it?

 

Q. How many bugs remain in the Async CTP Refresh?

A. That's hard to know. Our QA efforts have been dedicated to testing our async codebase that will be part of the next Visual Studio. We've spent far less time testing the CTP. There might be bugs in the CTP that were never visible in the real codebase. There might be (there were) bugs in the real codebase that never impacted the CTP.

The best I can do is give you an indication of the asyncs compiler bugs we've found, and which aspects of the async feature they're associated with. From this you'll get an idea of which code patterns might be more exposed to bugs.

Here's an overview of the all the async bugs we've logged. The green ones have been fixed in the original Async CTP or in the CTP Refresh. All the rest have remain unaddressed in the CTP Refresh.

 

 

 

Bad codegen bugs

The red segment of the pie chart is where our main codebase has, at some stage in development, produced bad codegen for certain async coding patterns, and hasn't been fixed in the CTP Refresh, and isn't in one of the main "unsupported-for-CTP" areas.

All the bugs we've seen in the red category stem from nested awaits. A 'nested await' is one that appears as a sub-expression of a larger expression. For instance,

var x = await f() + await g();

 

The CTP uses an incorrect order of evaluation for nested awaits. In particular, the CTP will first evaluate all Awaits in a given statement in order; once it has evaluated them all, then it will evaluate the remainder of the expression.

For the final version of async, which will be shipped in the next Visual Studio, we had to make a major overhaul of the codegen for nested awaits to fix the order of evaluation.

All of the bugs in this red category were associated in some way either with the CTP's treatment of nested awaits, or with the overhaul of them. Even some code patterns that don't use nested awaits (such as VB "Continue" out of a "Do / Loop Until") suffer bugs that come from the CTP's incorrect order of evaluation.

 

' BUG: VB-only bad behavior in async/iterator methods even without nested awaits.
' SUGGESTION: use PEVerify and run unit-tests

Do
    If
True Then Continue Do
Loop
Until True

Dim i = 1
For i = 0 To 100
    Console.WriteLine(i)
Next

' BUG: VB-only For loops don't work if you have non-constant "steps"
' SUGGESTION: Rewrite non-constant Steps into While loops.
' (Actually, this is the only bug that's not related to nested awaits...)
Dim
 s = -1
For i = 10 ToStep s
    Yield
i
Next


// BUG: VB and C# bugs calling MyBase in async method (especially with generic params)
// SUGGESTION: use PEVerify to catch these

base.Foo<T>();


' BUG: Nested awaits cause VB-only incorrect behavior in flow-control-constructs
' SUGGESTION: Avoid nested awaits in these conditionals
Do
   
Console.WriteLine("a")
Loop Until Await f()

Do
While Await f()
    Console.WriteLine("a")
Loop

If
11 = Await f(10) Then
    Console.WriteLine("a")
ElseIf 10 = Await f(10) Then
    Console.WriteLine("b")
End If

For i = Await f(1) To Await f(2) Step Await f(3)
    Console.WriteLine(i)
Next


' BUG: Nested awaits are dodgy in both VB and C# in these situations
' SUGGESTION: use PEVerify and unit tests to check you’re getting right behavior
Dim x = New With {.A = Await f(1)}

var c2 = new int[] {await f(1), await f(2) };

Dim s = (Await f()) & Await f()

h(await f(1));  // when passed to a “ParamArray” parameter of h()

 

 

 

Bugs in language/IDE areas unsupported by the CTP

The lower segment of the above pie chart was about areas that are just not supported by the CTP. Here are those areas in detail. They're mostly easy to avoid.

 

 

Contextual keywords - the final implementation of Async will treat "async/await" as contextual keywords in C#, and "Async/Await/Iterator/Yield" as contextual keywords in VB. We have had many bugs in this area, but none of them should affect the CTP - since they are all simply reserved words in the CTP.

Overload resolution - the final implementation of Async has a host of new rules for overload resolution and type inference. This was a difficult area to implement and test, and none of the changes have been back-ported to the CTP. When you pass an async or iterator lambda as an argument to a method, the CTP will usually fail to do the correct type inference, and will usually fail to pick the correct overload.

Late-bound await - the final implementation of Async will allow late-bound await, i.e. "Await Object" in VB and "await dynamic" in C#. We had a fair number of bugs in this area. The CTP simply gives an error on these cases, so bypassing all the bugs.

Await in unsafe code - the final implementation of Async will allow async methods to have unsafe code blocks, just so long as await is never itself in an unsafe context. None of this has been back-ported or tested in the CTP. You should avoid mixing await/async with unsafe in the CTP.

Await in expression lambdas - the final implementation of Async will allow expression lambdas. The CTP doesn't.

IDE niceties - the final implementation of Async will have proper intellisense, quick-fixes, GenerateFromUsage, GoToDefinition and so on. None of these are present in the CTP.

Debugger - the final implementation of Async will have debugger support. It is absent in the CTP.

Nicer error messages - We filed many bugs and made lots of effort to improve the quality of error messages for erroneous uses of await. None of this work has been back-ported to the CTP, which remains stuck with rather poor error messages.

Bugs in illegal code - There are many places where the CTP compiler has not yet been hardened against erroneous uses of await. The good news is that if your code is correct, then you won't run into any of these bugs! The bad news is that you may inadvertently write erroneous code, and get VS crashes or PEVerify failures or incorrect codegen. Here are some of the erroneous code that triggers these bugs:

  • incorrect implementation of the awaiter pattern, or one with constraints
  • multiple or missing references to AsyncCtpLibrary.dll
  • async constructors or async Main
  • ByRef parameters in async/iterator methods
  • On Error Goto or Resume in async methods
  • async methods with __refvalue or __argiterator params
  • await of restricted types such as argiterator/typereference
  • use of await in attribute arguments
  • use of yield/await in catch/finally blocks
  • Also, devious-but-not-illegal implementation of the await pattern may trigger bugs:

' BUG: VB and C#, awaitable pattern with partial/conditional/obsolete pattern methods
' SUGGESTION: implement the pattern straightforwardly

Dim t As MyAwaitable
Await t

 

Miscellaneous bugs and workitems

Here are the rest of the bugs and workitems. None of these affect the quality of code you produce using the Async CTP.

 

The few IDE crashes were nebulous and not reproducible. If you run into any, please let us know! The only ones we've been able to pin down were all reported too late to be fixed in the CTP:

  • VB: Trying to do a late-bound await in a VBCore project
  • VB: Making a task-returning async event handler
  • C#: hovering over a named indexer that is awaited and then invoked in one expression