This project is read-only.

VB LDM 2014-02-17

Topics: VB Language Design
Mar 28, 2014 at 11:50 PM
Edited Mar 29, 2014 at 1:09 AM
VB Language Design Meeting 2014-02-17


Please don't reply to this post with comments on individual language features. That will get out of hand because there are so many of them. If you have comments on an individual feature, please create a new post for it. If the feature's also in C#, then cross-post to the "C# language design" topic.

We spent this meeting on a complete roll-up of all the Dev14 ideas that have been floated and designed so far. This isn't a commitment that we will have time to finish designing or finish implementing them. It's just where we are at the moment.

The notes from this meeting are deliberately "RAW". I haven't spent time to type them up neatly. My goal is to get all language notes out in the open, where the public can see them, with as little process or hindrance as possible. I figure it's better to publish early and iterate (and fix typos), rather than publish late. If you see anything that seems wrong or you disagree with, please first seek clarification rather than taking these notes as Microsoft's definitive statement on anything.

You might notice that I've written the date in ISO format, "2014-02-17". This is in honor of a new VB language feature that has already been implemented, "year-first date literals".

1. Improved XML doc-comments.

Approved. Already in Main. Parity with C#.
These are now supported to the same extent as C#, e.g. crefs and paramrefs are parsed correctly by the language. (The IDE team also added a much richer IDE experience will full syntax colorization, quick-info and rename support.

2. Comments after implicit line-contuations.

Approved. Already in Main. VB-specific.
Comments are allowed after implicit line-continuation characters. Here are examples which failed to compile before, but now work:
Dim invites = {"Jim",   ' got to invite him!
               "Marcy", ' Jim's wife

Dim addrs = From i In invites     ' go through list
            Let addr = Lookup(i)  ' look it up
            Select i,addr

3. #Region anywhere

  • Approved. Already in Main. Parity with C#.*
"#Region" is allowed within method bodies and can cross method bodies.
Function Range(min%, max%) As IEnumerable(Of Integer)
    If min > max Then Throw New ArgumentException
#Region "validation"
    Return Helper(min, max)
End Function

Private Iterator Function Helper(min%, max%) As IEnumerable(Of Integer)
#End Region
    For i = min To max
        Yield i
End Function

4. Implement readonly props with readwrite props.

  • Approved. Already in Main. Parity with C#.*
The following code used to give an error saying that C.p doesn't match I.p and hence can't implement it. But now it's okay. (Also, you can implement writeonly props with readonly props)
    Interface I
        ReadOnly Property p As Integer
    End Interface

    Class C : Implements I
        Public Property p As Integer Implements I.p
            End Get
            Set(value As Integer)
            End Set
        End Property
    End Class

5. Overloads Overrides

  • Approved. Already in Main. Interop with C#.*
Previously, VB libraries had to write both modifiers "Overrides Overloads" to play nice with C# users. Now, "Overrides" members are also implicitly Overloads.

6. Allow CObj in attributes

  • Approved. Already in Main. Parity with C#.*
The following code used to give an error that CObj(...) wasn't a constant. But it is, so we no longer give the error.

7. TypeOf IsNot

  • Approved. Already in Main. VB-specific.*
You used to have to write it the long way:
   If Not TypeOf sender Is Button Then
Now you can write it more readably:
   If TypeOf sender IsNot Button Then
You could already use the "IsNot" operator previously, but only for reference comparisons e.g. "If sender IsNot Nothing Then"...

8. Readonly autoprops

  • Approved, but design-questions remain. This has been prototyped. Aligns with C# vNext "getter-only and initialized autoprops" feature.*
We all know that we want this, but we don't know exactly what we want... Here’s an example:
   ReadOnly Property p As Integer = 15
   ReadOnly Property q As Integer

   Sub New(q As Integer)
      Me.q = q
   End Sub
It’s clear that “ReadOnly” means that the property itself only has a getter. That’s the only thing we’re agreed upon at the moment…

Outstanding questions:
  1. Should the backing fields be visible in the designer, or hidden like they are now?
  2. Is the backing field ReadOnly like C#, or mutable like a current autoprop?
  3. Can you assign to a ReadOnly autoprop in the constructor (implicitly an assignment to the backing field) just like you can assign to a ReadOnly field in the constructor?
Note1: The CLR allows an assignment-to-readonly instruction at any time, but it has no effect after the constructor has finished. The CLR allows assignment-to-readonly by reflection at any time.

Note2: Why would you want to assign to a ReadOnly in the constructor rather than just in the property’s initializer? … Maybe it’s because you need some control-flow logic to figure out what to assign, e.g. a For loop. Maybe it’s because you assign based on arguments that were passed to the constructor – although this latter case is addressed by primary constructors.

Here is some previous public feedback on the topic:

Here are three mutually incompatible visions of the feature:

PROPOSAL “A”: ReadOnly autoprop is like ReadOnly field. We are minimizing the code you need to type to get readonly properties.
Backing field is hidden from intellisense, the backing field is ReadOnly, and you can assign to a ReadOnly autoprop in the constructor. (This is like C#’s current proposal, except C# proposal doesn’t allow assignment in the constructor).

PROPOSAL “B”: ReadOnly autoprop is like existing autoprop but without setter. We are sticking closely to the status quo, not doing anything special.
Backing field is hidden from intellisense, the backing field is ReadWrite, and you can’t assign to the autoprop in the constructor. (Note: this proposal is pretty weak unless there are primary constructors. Also, the existing VB snippets for readonly properties use mutable backing fields).

PROPOSAL “C”: Backing fields should become first-class and well-known. They’re VB’s answer to “{get; private set}” from C#.
Backing field is shown in intellisense, the backing field is ReadWrite, and you can’t assign to the autoprop in the constructor; but you can assign to the backing field.

MVP feedback: Bill McCarthy, Jan Zaruba, Joacim Andersson, Klaus Loffelman all voted in favor of proposal “C”. No one voiced any other preferences. We have implemented proposal "C" in the preview, and are open to further feedback.

9. International date literals

  • Approved. Already in prototype. VB-specific.*
Design is merely to offer ISO syntax for the range of things that are covered by existing date literals for DateTime (so: no timezone, no milliseconds, no DateTimeOffset). The format for dates is YYYY-MM-DD, and they must have the full number of digits.
   Dim d = #2013-06-12#
   Dim d = #2013-06-12 15:17:23#
Q0. What about the horrible hacks that Anthony discovered, about how the IDE compiler allows two-digit dates (but command-line doesn't), and … ???

Q1. Do you allow #2014-02-04 24:00# as well as #2014-02-05 00:00# ?
A1. The current DateTime constructor does not allow you to pass “24” for the hours. And the current VB literal doesn’t allow 24:00 as a time. So we should disallow this.

Q2. Do these always produce a datetime with .Kind = DateTimeKind.Unspecified?
A2. Yes, since current VB DateTime constants always do.

Q3. Do we intend to come up with a solution for declaring an optional parameter with value “DateTime.MaxValue”? Currently you can’t, because “DateTime.MaxValue” isn’t a constant, and there’s no way to write DateTime.MaxValue in the VB datetime literal format.
A3. No, no solution.

Q4. Can we have the existing DateTime separators, or merely the ISO separator (-) ?

Q5. Do we allow AM/PM or do we require 24 hour clock?

PROPOSAL “A”. This feature is just "existing DateTime literals but with four-digit year first"
Q4 = allow existing separators, Q5 = allow AM/PM

PROPOSAL “B”: This feature is about unambiguous universal (hyphen, year-first) notation for the date which was the part that SUFFERED, but just use existing time syntax for the rest. (votes in favor: Aleksey, Klaus, Lucian)
Q4 = require hyphen; Q5 = allow AM/PM

PROPOSAL “C”: This feature is "subset of ISO syntax for those things encompassed by current DateTime literals. (votes in favor: Neal, Dustin, Anthony) Everyone knows ISO syntax. They won’t need to stop to wonder whether #2010/10/11# means November 10th or October 11th (although there are no cultures in the world where it means November 10th).
Q4 = require hyphen, Q5 = disallow AM/PM

We could also accept AM/PM, but (1) the prettylister prints into ISO format, and (2) we give a warning.

RESOLUTION: proposal "A", based on discussion from 2014-02-04. Do you see what I did there? :)
Marked as answer by lwischik on 4/3/2014 at 10:31 AM
Mar 29, 2014 at 12:14 AM
Edited Mar 29, 2014 at 1:26 AM

10. Digit group separators

*Approved, but needs further language design decisions. Already in preview. Aligns with C# vNext feature "digit grouping"

We settled on spaces as separators per ISO recommendations, but C# preferred underscores (like Java), so we should revisit that difference.
   Dim x = &B1110 0001 1010 1111
   Dim x = &B1110_0001_1010_1111_SB
Design: this is a token-level thing. You can’t have line-continuations (implicit or explicit). For spaces, we’d only allow a single space. Not sure what we’d do with underscores. You can put the separator anywhere you want, including after &H and &O and &B, except after the &.

Current experimental prototype allows space, underscore and backtick. Note: old calculators in the 1970s used backticks! No one likes the backtick (apart from Klaus).

PROPOSAL 1a: the only separator allowed is underscore.

PROPOSAL 1b: allow both space and underscore.

PROPOSAL 2: separator is allowed before the type suffix

PROPOSAL 3: not allowed more than one underscore in a row

Open question: should you be allowed a separator before the numeric-type-suffix? E.g.
Dim x = &B 1110 S

11. Binary literals

*Approved. Already in preview. Aligns with C# vNext feature "binary literals" *

It will be nice for enum literals. F# has them. We would ask the debugger to consider displaying stuff in binary as well, but that’s lower priority.
   Dim x = &B11100101
Note: Integer.TryParse is a language-neutral .NET Framework function, that should not be augmented just for sake of new language-syntax. So we wouldn't want to make it work with the above syntax.

12. Multiline strings

*Approved. Already in preview. Parity with C#. *
   Dim x = "Hello
We will use the normal quotes. We are happy that the pretty-lister inserts them in pairs in Roslyn (so the remainder of the file doesn't blink on and off too much).

Note: the pretty-lister should not auto-indent the next line when you press ENTER. (C# doesn't either)

Q. Does a newline emit CRLF?
A. It's whatever is in the source file. That's how C# works.

We allow any characters up until an unpaired " (or all the other string-starting-and-closing quote characters are currently allowed).

Q. Do we allow multiline string literals in preprocessor directives?
A. No. Neither Roslyn nor Native allow implicit line continuations in preprocessor directives. (although they sadly allow explicit line continuations).

12. Select Case Typeof

Approved, but needs further language design decisions. Already in preview. VB-specific.

Anthony's basic idea, which is in the preview:
Select Case sender
   Case 3 : ...
   Case < 4 : ...
   Case As UIElement : ...   ' now works
   Case b As Button : ...    ' now works
   Case t As TextBlock: ... ' now works
   Case As UIElement, As Integer : ...    ' okay in Design1
   Case 3, As String : ...                ' okay in Design1
   Case ex As Exception When ex.i<7 : ... ' not supported
   Case c As Control, d As DropDown : ... ' error.
   Case AsNot Integer: ...                ' not supported 
End Case
Questions arising from the prototyping effort:
  • Value types
  • When clauses - Should they apply to each clause or each Case? Should they be generalized to any kind of Case clause? Should they be removed? Should we add a Continue Select as an alternative?
Design1: "As String" is just a normal case, like the other cases in VB, and indeed you can combine them and jump into them. "x As String" is different: it must be the only condition on the Case line, and doesn't admit inbound jumps (similar to how For blocks don't admit inbound jumps). The scope of the variable declaration is just that case clause, and follows the same principles as other blocks which define variables like ForEach.
   Case 3, As String : ...
   Case b As Button : ...
Design2: both "As String and "x As String cannot be comma-combined with other case clauses, and neither admits inbound jumps. The motive is to safeguard a possible future use of comma in pattern-matching. (Unfortunate, since VB doesn't have "case fall-through", and so likes to use comma to combine cases).

Concern: It feels like generics will be important. How to implement them though? Answer: this is impossible in the current CLR without reflection, and we wouldn’t want a language feature that depended on reflection. Therefore this scenario is out of consideration.
   Case As IEnumerable(Of T) : fred(Of T)(Nothing)
Concern: We have to make sure that anything we support now will be compatible with a hypothetical future that allows richer pattern-matching. So let's design that hypothetical future right now even though we won't implement it. Pattern-matching is odd in a language without algebraic datatypes, because there's no inbuilt canonical way to decompose an object. Possibilities are (1) only do decomposition on types with primary constructors; (2) decompose based on property names; (3) like Scala a type has a canonical "Unapply" method which says how to decompose; it might be an extension method and might be overloaded by type or arity; (4) like F# have active patterns.

In our investigation of pattern-matching, we're heavily swayed by Martin Odersky's paper

Anthony's advanced idea:
Dim x = sender Matches Dim ui As UIElement When ui.Tag = "a"

Select Case sender
   Case Matches Dim b As Button : ...
End Select
Expr ::= … | expr Matches M [when boolean-expr]
M ::= * | Dim v | Dim v As T | MatchFunction(M, …)
Scope: the scope of each introduced "v" is the containing statement. So, it works in "If" and "Case", but it doesn't help with "Dim x = e Matches Dim v"

OPTION1: can we allow arbitrary Boolean expressions inside M? Still can't see it.

OPTION2: can we remove "Dim", and make the parentheses on MatchFunction compulsory? i.e.
expr ::= … | e match M1
M1 ::= M2 [where Boolean-expr]
M2 ::= * | Dim v | [Dim] v As T | MatchFunction(M1, …) 
OPTION3: Should we also we also allow M2 ::= … | v As MatchFunction(M1, …)

TO RECAPTURE the basic question we're trying to address...

How difficult is it to match Tree(Leaf(2), Leaf(3))


(1) How exactly will MatchFunction bind? It would be nice to have a "matcher" object that we can dispatch on. It would be nice to dispatch on Integer.TryParse. Or maybe each active pattern should be explicitly indicated with an attribute (like extension methods).

(2) The whole idea here is to match against the runtime type of expr. How does that even work when invoking MatchFunctions? Overload resolution? It suggests putting overload resolution into the late-binder, which we don't like.

14. Await in Catch and Finally blocks

Approved. Aligns with C# vNext feature

We'll leave the design work to C# LDM, and do exactly the same.

15. Type inference for constructors of generic types.

Approved. Aligns with C# vNext feature
   Dim x As New List({1,2,3})
   Dim x = New List({1,2,3})
We'll use just the same semantics as C#.

Note: it doesn't help common cases that I run into, e.g.
Function f() As Tuple(Of Integer, String)
   If condition Then Return New Tuple(Nothing, Nothing)
   Return New Tuple(5, "hello")
End Function
But it's better than nothing, and brings more regularity into the language.

16. <field:attr> on autoprops

*Approved. Aligns with C# vNext feature "field target on autoprops" *
   <field:attr> property x As Integer
   <field:attr> event e As Action
Stylistic guidance will be to use <field:NonSerializable> on events even though the field target isn't needed. No further semantic design work needed.

17. Allow scalar query expressions to clash names.

Approved. Parity with C#

There are two parts of the approved design....
Dim args As String()
Dim q = From arg In args Select arg.ToString()
' BC36606: Range variable name cannot match the name of a member of the 'Object' class
Design1: Improve error message so it says “Inferred range variable name ‘ToString’ cannot conflict with blahblahblah”.

Design2: If the last Select is followed only by the innocuous clauses (Distinct, Skip, Take) and if the Select has only one item, then skip name generation entirely.
Mar 29, 2014 at 12:28 AM

18. Introduce "Null" literal

  • Rejected on 2014-01-06. It would have had parity with C# "inference is aware of null" feature. *
Here is the problem statement:

“I don’t know what Nothing means.” I wrote the following code, expecting that Nothing would mean a nullable integer, but it came back as the integer 0.
10:   Dim x = If(False, 0, Nothing)
“Nothing doesn’t mean what I think it means.” I wrote the following code, expecting that by specifying the type, it would know to use a nullable nothing. But it didn’t.
20:   Dim x As Integer? = If(True, 0, Nothing)
“No one else knows what Nothing means.” MSDN articles always say “Use null (Nothing in VB),” but that’s not really correct. Nothing is more like default(T), and VB lacks null.


The basic problem is that Nothing was a bad primitive from the get-go, and C#’s “null” was a better primitive. We should encourage people, as best-practice, to use “null”. It would work as follows.

Syntax: if we are parsing an expression and encounter the simple name “Null” and it doesn’t bind to any namespace, type or variable, and doesn’t have qualification or indexing or dictionary-lookup operators after it, then treat it as “Null”.

Semantics: Null is identical to Nothing, except for the following rules:
  1. It is an error to interpret Null in a context expecting a non-nullable value type. So, “Dim x As Integer = Null” is an error, but “Dim x As Integer? = Null” is okay.
  2. Even though Null has no inherent type of its own (i.e. Null doesn’t suggest its own type to dominant-type algorithm), nevertheless "Dim x = Null" infers type Object for x. This is similar to the existing rule that "Dim x = Nothing" infers Object even with Option Strict On.
  3. If the compiler needs to determine dominant-type-of-set-of-expressions, and one of those expressions is a naked Null literal, then when gathering the set of inherent types of all those expressions, replace each non-nullable-value-type candidate “T” with Nullable(Of T).
Example: "Dim x = If(b,5,Null)" previously gathered only one inherent type "Integer" as a candidate for dominant-type-inference. From rule [3] it will instead gather “Integer?” as a candidate. Both expressions can be interpreted as Integer?, so that succeeds.

Example: “Dim x = {1,2,Null}” previously gathered only one inherent type “Integer” as a candidate for dominant-type-inference. From rule [3] it will instead gather Integer? as a candidate, and it works.

Example: “Dim x = {1, “hello”, Null}” previously gathered Integer and String as candidates. From rule [3] it will instead gather Integer?, String as candidates. There is no dominant type amongst these two, so it falls back to Warning: Object Inferred.

Example: “Dim x = {1L, 2, Null}” previously gathered Integer and Long. From rule [3] it will gather Integer? and Long?. The dominant type amongst these two is Long?, which is what it will pick.

Incidentally, the VB compiler up to VS2013 already recognizes syntactic “null” in the same way, in order to produce a special-case error:

We did consider “Nothing?” instead of “Null”. That has the slight advantage that Nothing is already a reserved word in VB. However, it looks strange, and will leave people even more confused. We also considered “Nuffink” :) and rejected it -- it’s not all dour-faced diligence in the VB LDM!

19. Smarter name resolution

  • Approved. Already in Main. VB-specific. *
Full notes are recorded in the minutes of the VB LDM 2013-12-13. I haven't typed them up onto codeplex yet. If you want me to, please request them.
Import NA     ' contains only the type NA.N1.T
Import NB     ' contains only the type NB.N1.U
Dim x As N1.T ' let this resolve to NA.N1.T

Dim x As ComponentModel.INotifyPropertyChanged
' In Console is okay, but in Winforms apps it is ambiguous
' between System.ComponentModel and System.Windows.Forms.ComponentModel
' Note: System.ComponentModel isn’t project-imported on most project types
' so the full qualification might be expected.

' In Console and Winforms this is okay, but in WPF aps it is ambiguous
' between System.Threading and System.Windows.Threading
' Note: System.Threading isn’t project-imported on most project types
' so the full qualification might be expected.

Dim y As Xml.Linq.Xdocument
' If you import System.Security.Cryptography, this becomes ambiguous
' between System.Xml and System.Security.Cryptography.Xml
' Note: System.Xml isn’t project-imported on phone/silverlight,
' so the full qualification might be expected.

' In Silverlight/Phone this works, but in WinRT aps it is ambiguous
' between System.Diagnostics and Windows.Foundation.Diagnostics
' Note: System.Diagnostics isn’t project-imported on phone/silverlight,
' so the full qualification might be expected.

20. Partial modules and interfaces

  • Approved. Aligns with C# vNext feature "allow partial anywhere" *
   Partial Module Module1
   End Module
Partial methods inside will be allowed; they will just drop out. We can't think of any design gotchas. This was a request from the Roslyn implementors.

21. Params IEnumerable

  • Approved. Aligns with C# vNext feature. *
   Sub f(ParamArray x As IEnumerable(Of Integer))
No further design work needed.

22. Private Protected.

  • Approved, but syntax needs to be settled. Aligns with C# vNext feature.*
Design option 1: like C#
   Private Protected Sub f()

   Friend Property p As T
     Private Protected Set
   End Property
Design option 2: not like C#, but it is conceptually cleaner. VB should naturally be more verbose where verbosity is actively useful.
   Protected And Friend Sub f()

   Friend Property p As T
     Protected Set
       ' means anyone in the assembly can see it,
       ' but only those who also derive can set it
   End Property

   Protected Property p As T
     Friend Set
       ' means it's a protected property, but
       ' only my assembly can set it
   End Property
RESOLUTION: Please see meeting notes from 2014-03, where we opted for "Design 3". (I haven't typed up those notes yet. If you'd like to see them, let me know -- lwischik).

23. TryCast for nullable value types

  • Approved. Parity with C#.*
TryCast will allow a target type that's either Reference type or a nullable type. It will work exactly as in C#. Note that TryCast bypasses the latebinder.
   Dim obj As Object = 1
   Dim x = TryCast(obj,Integer?)

24. Override events

  • Approved in principle, but still needs design work. This is a parity issue for BCL and other frameworks*
Note: it is CLS-compliant for the BCL to do this. The only CLS restrictions around “abstract” are with generics.

Currently C# lets you
  1. Define an interface with an event.
  2. Implement the interface by providing that event
  3. Define an abstract class with a MustOverride event
  4. Inherit from that class and override the event
  5. Define a class with an Overridable event
  6. (there’s a C# bug when you try to inherit from that class and override the event, but there are workarounds)
VB lets you do (1) and (2). This means that VB users are shut out from any library which uses (3) or (5): VB users are unable to inherit from the class.
In .NET4, the BCL folk have changed a few classes to include MustOverride events. For instance, Inherits System.Windows.Documents.Serialization.SerializerWriter. The only VB workaround is mock-type injection voodoo. (e.g. also the VS Editor folks, writing in C#, had done this themselves).

VB's lacuna was just an oversight.

Proposal at hand is (3) let you define an abstract class with MustOverride event, (4) let you inherit from that class and override the event, (5) let you define a class with an overridable event.

25. Allow MyBase in structures

  • Approved, but low priority. VB-specific. *
The example is MyBase.GetHashCode(). It should behave like you'd expect, like C# does.

NB. Structure "GetHashCode" finds the first instance field and calls GetHashCode on it, using reflection 

26. Allow delegates in object initializers

  • Approved in principle if C# goes ahead with the same feature, but needs further work on syntax*
Dim x As New C With {.Name = "x", .Notify += delegate}
Dim x As New C With {.Name = "x", AddHandler .Notify, delegate }
Dim x As New C With {.Name = "x", .Notify = delegate }
Dim x As New C With {.Name = "x", .Notify := delegate }
We're not sure what syntax to use…
+= is the C# syntax and resonates with our proposal to add += for delegates, even though the feature only allows you to sign up a single delegate
= is the XAML syntax, and nice and short. If the constructor of C had already signed up a delegate, it'd be weird! It's also nice not to add new syntax.
:= is the named-argument syntax

NOTE: maybe there are problems to be solved at the same time for nested collection initializers, and of course lightweight-dynamic initialization. C# lets you use assignment for collection-initializers:
var x = new Order { .Items = {1,2,3} }
This gets the value of "Items" and then does a collection-initializer on it. We can't do this. But it shows there exists in C# a precedent where = really means Add.

29. Allow With and From together

  • Approved, but low priority. VB-specific. *
This is just cleaning up the language. Design is that "With" comes first and "From" comes next.
   Dim x As New List(Of Int) With {.Capacity=10} From {1,2}

30. Extern alias for assemblies

  • Undecided.*
We have a design doc for this, but it's a lot of work, and seems low-priority.
Mar 29, 2014 at 12:36 AM

31. Combine delegates with +=

  • Approved in principle, but need design work. Parity with C#. *
   m_ClickEventHandler += value
   m_ClickEventHandler -= handler
Semantics will be exactly as for C#. However we will not also allow "event += value" as a synonym for "AddHandler event, value".

STILL TO DO: figure out exact semantics for "+"
m_ClickEventHandler = m_CLickEventHandler + value
STILL TO DO: figure out (for this as for all new features) how much should make it into the late-binder?

32. String interpolation

  • Undecided *
   Dim x = $"hello \{e.Name}"
We have detailed notes from this from VB LDM 2013-12-09. It's a complicated topic. If you'd like me to type up the notes, let me know. -- lwischik

42. Out parameters and implicit Out parameter declaration

  • Approved in principle, but needs design work. Aligns with C# "out vars" feature.*
   ' Callsite:
   If Integer.TryParse(s, Out [Dim] x [As Integer]) Then ...

   ' Declaration-site:
   Function TryParse(s As String, Out x As Integer) As Boolean
The declaration-site will be emitted as <Out> Byref. However if you merely write <Out> Byref then it will be treated as a ByRef rather than as an Out, because the compiler doesn't generally crack attributes in source code.

The declaration method will default-initialize all Out parameters on entry (this can be optimized away by the compiler). Warnings will be emitted for both use-before-assign and return-without-assign.

On the callsite, for back-compat, you don't need an "Out/Output" keyword to match an out parameter. However, you do need the keyword in order to declare the variable inline, and if you use the keyword then it is an error to pass it something that's not an lvalue.

Outstanding questions:
  • What should the scope of "x" be? C# LDM is also wrestling with this, and the answers are sometimes unexpected.
  • Should the pretty-lister insert "Out" as you type a function invocation?
  • Callsite syntax: should it be "Out" or "Output"? Should we use rules similar to the existing "For" where you don't need Dim, and instead a new variable is implicitly declared if there's an "As" clause or if it doesn't bind an existing name?
  • Is this synergy with "For" a step too far? Given that the scope of the out in statements will bleed outside. Shouldn't we help people avoid the dangerous situation?

34. Declaration expressions

  • Rejected. This would have had parity with C# vNext feature. *
This is a generalization of Out Dim, to allow variables to be declared in any expression.
Console.WriteLine(Dim x As Integer = GetValue(), x + 2)

If (Dim c = TryCast(x, String)) IsNot Nothing Then ...
var z = (var x=LengthyGetX(); var y = LengthyGetY(); x*x+y*y);

If (Let c = TryCast(x, String) in c) IsNot Nothing Then

var z = Let x = LengthGetX()
        Let y = LengthyGetY()
        Returns x*x + y*y

If (Let c = TryCast(x,String)) IsNot Nothing

var z = Let x = LengthGetX()
        Let y = x+LengthyGetY()
        Select x*x + y*y ' DO NOT want x to escape scope
"Select"? Yield? Returns? In?
var z = Let x = LengthGetX(),
        y = x+LengthyGetY()
        Yield x*x + y*y ' DO NOT want x to escape scope

Integer.TryParse("15", Out x)
Console.WriteLine(x) ' REALLY DO WANT x to escape scope
C# is using the same mechanism for both "out vars" and the "let" construct. Maybe that's why C# scope isn't always obvious?

Q. Does the keyword "Let" cause ambiguities if we're using this inside query expressions? Note that "Let" is bad for query expressions.

Note: one thing that makes sense for C# is that C# already allowed assignment into expressions, and so assigning to a new-variable is a sensible extension. But VB has never allowed assignment in expressions, so it doesn't naturally go that far.

RESOLUTION: None of this feels naturally "VB"ish. We're happy if VB sticks merely to Out parameters and implicit declaration of Out arguments.

35. Expression sequences

  • Rejected. Would have had parity with C# vNext feature. *
The C# goal is to allow multiple expressions. "An expression can be of the form (expr1; expr2; expr3) which has the end result "expr3" but it evaluates (and discards) the preceding expressions in the list."

VB proposal:
Dim x = (Console.WriteLine("log") : 15)
Dim x = Do Console.WriteLine("log") In 15
an expression can have the form "Do statementlist In expr" which has the end result "expr" but it performs the preceding statementlist first.
Dim z = Do Dim x = sideeffect() : Dim y = sideeffect() In x+y

Dim z = Do
                 Dim x = sideeffects()
           In 15

Dim x = Function()
                 Console.WriteLine("log") : Return 15
              End Function.Invoke()
RESOLUTION: If we don't have declaration expressions, then there's little value in expression-sequences. It might make sense to combine the two into a "Let" operator, but that doesn't feel very VBish.
Mar 29, 2014 at 12:44 AM

37. Implicit Interfaces

  • Tentatively approved, but still needs design work. Parity with C#. *
One typical scenario for it is code-generators and partial classes.
Class C : implements IDisposable
   Sub Dispose() : End Sub
End Class
Proposal is to allow you to avoid use of "Implements" keyword. Note that C# also lets inherited members implement interface members. We might not do the same.

But would there be breaking changes? … yes if the base case implements stuff. Here's the example:
Class A
   Sub Dispose() : End Sub
End Class

Class B : Implements IDisposable
   Sub Dispose() Implements IDisposable.Dispose
   End Sub
End Class

Class C : Inherits B : Implements IDisposable
   ' Q. does C implement IDisposable.Dispose over again, or rely on base class?
  ' Currently works fine, relying on B's implementation of IDisposable
  Sub Dispose() : End Sub
End Class

Class D  : Inherits A : Implements IDisposable
   ' Q. Does C implement IDisposable.Dispose via the base class, or not?
   ' Currently an error
End Class

Class E : Inherits B : Implements IDisposable
  Sub Dispose() : End Sub
   ' Q. does E implement IDisposable.Dispose via its method, or rely on base class?
  ' Currently works fine, but it does NOT use E's "dispose" to implement the interface
End Class
Proposal1: If existing VB fails to come up with a method that implements the interface member, THEN we look for an implicit implementation of it. (Worry: if someone modifies the base class to now implement the interface method, then the derived class will no longer be implicitly implementing it. But note that it's already the case that base-class-authors can do a whole load of things that mess up your code.) If something gets hijacked, then give a warning.

Problem: principle is that "adding interface-implementation to a base case should not be a breaking change". The above proposal would violate this principle. (Actually, the principle isn't fully upheld already. Consider a class "X"
Sub f(dummy As Y) ' X has a narrowing conversion to Y
Sub f(dummy As I)
In the first release, it would implicitly pick the first overload. But if someone now changes X to implement I, then it would pick a different overload. So we don't believe that the principle is fully upheld. Maybe the principle is a smaller one. "If a new interface is added, and an existing class is changed to implement that interface, then it won't be a breaking change".

NB. Here's a "bridge-method" oddity:
class B { public void Dispose(); }
class C : B, IDisposable {}
// in C#, the CLR likes interface implementations to be virtual, so C# compiler creates a bridge method.
Proposal2: we'll look up implicitly by name, and always prefer the most derived type. But we'll add a warning when you would be hijacking code that already compiles. It's a warning, one that won't arise in typical case. (Note: you'd already have had to use Overloads/Shadows)
class A : IDisposable { Dispose }
class B : A {Dispose}
Class C : B, IDisposable {}
This code currently works, and does not hijack.
Proposal would change it so that C emits a warning and picks up the new one.

Proposal3: use new syntax, and copy the feature wholesale from C#.
Class B
   Implicitly Implements I1
   Auto Implements I2 
   Implicitly I3
   Implicit I4
End Class
' As with C#, it will use explicit implements if they're present, and if absent then it will start looking in the most derived type. Hijacking will be impossible. NB. Vladimir points out that the C# implementation doesn't quite match the C# spec.

RESOLUTION: Yes. Use Proposal3. We will look for a keyword combination that seems nice.

Note: it's a weird situation that the more desirable syntax "Implicitly Implements" is more verbose than the less desirable traditional syntax "Implements". There may be IDE codespit ameliorations.

NB. Like in C#, a single method can implement the same named member from multiple interfaces. (currently can with the Implements keyword).

Q. How discoverable will it be?

Q. Currently VB requires explicit interface implementation to have exactly the same optional parameter defaults. Should we adopt C# rules here? or VB? Note that C# came from a history that the camel's nose was already in the tent. Oh, and what if there are multiple interfaces with different default values?

RESOLUTION: Stick with current VB rules, and if they complain, we can look into it then. And refactoring makes C# a bit dangerous. Note: we should match based on signature, and then give an error if the defaults don't match.

38. GetName operator

  • Tentatively approved, but there are design issues. Parity with C# vNext "nameof" feature. *
Here are some sketch syntaxes we played around with...
Dim name = nameof(Point.X)
Dim name = GetName(Point.X(Of ,).Y)  ' we prefer GetName

DIm name = nameof(System.Action)

Dim TextProperty As DependencyProperty = DependencyProperty.Register(GetName(Text), GetType(String), GetType(MyClass))

Public Property Text As String
     Return CStr(GetValue(TextProperty))
   End Get
   End Set
End Property

Sub f(x As String)
   If x Is Nothing Then Throw New ArgumentException(GetName(x))
End Sub
The C# proposal is straightforward: the argument to nameof is an (optional type-or-namespace-followed-by-dot) and then an identifier. The result is the string of that identifier.

In VB we prefer "GetName" to "NameOf".

Q. Will it return the casing as you passed it to GetName, or will it return the casing as defined in metadata? What if there are two different casings in metadata? What if there are two different casings in metadata and they disagree with the argument to GetName? - Answer: it's an error. You'll have to fall back to a string literal.

Q. In VB can we refer to the constructor "New"? - No, there's never any need for it.

Q. Can "Point" be ambiguous? What if "Point" is a namespace, and member "X" exists in multiple modules inside that namespace?

Proposal1: "Point" (the prefix) must be unambiguous, but it's fine for "X" to refer to a method-group or a property-group or a unique thing. This is the current name lookup rules.

Q. Should we allow unbound generic types, similar to GetType?
A. Yes.

Q. Can we use type keywords like "Integer" and "Boolean"? (same question for C#).

Q. If we GetName() on an alias, does it refer to the underlying metadata name? Presumably yes!

GENERAL FEEDBACK: Concern from Aleksey and IDE team about overloads and dealing with ambiguity. E.g. when you do find-all-references, or rename-refactor on an overloaded thing, then it's not clear which of the overloads "nameof(...)" operator is referring to (hence not clear whether it should be renamed). And the Roslyn APIs that return reference-counts -- how should they deal with it?

(1) GetName( BLARGH . Identifier ) where GetType(BLARGH) is valid
(2) GetName( current name lookup rules ) and the name lookup must be successful. (but need it also be unambiguous?)

39. Extension statics

This topic has been discussed offline. Let me (lwischik) know if you'd like me to type up the notes.
Mar 29, 2014 at 12:50 AM

40. Expression-bodied function members

  • Rejected. Would have had parity with C# vNext feature.*
In C# it'll look like this:
public int R => Math.Sqrt(X*X + Y*Y);  // readonly autoprop
int M(int i) => i+1;  // methods
static operator int(C x) => (int)x.field; // conversions
static operator +(C x,C y) => new C(x.field + y.field);
C#: all of these things are expressions, but statement-expression is a kind of expression, so they allow all statement-expressions as well (including void-returning statement expressions).

Is there anything to do in VB?

VB single-line lambdas:
   Dim x = Function(y) y+15
   Dim z = Sub(y) Console.WriteLine(y)

Class C
  Public Square(x As Integer) x*x
End Class
Proposal 1: allow more things on a single line... We're already part way there (Roslyn now no longer requires "End" to start a line). It just requires allowing Sub() and other things to have a colon following them.
   Dim lambda = Sub() : Return 5 : End Sub
   Sub method() : Console.WriteLine("oops") : End Sub
   Property p As Integer
     Get : Return _p : End Get
     Set : _p = value : End Set
   End Property
(Q. are there any ambiguities between multiline lambdas inside methods? Follow-up with Tomas/Anthony for more…)

Proposal 2: Invent a special-case syntax single-line functions, methods, operators and readonly autoprops.
  Function f() As Integer : Return 15
  Sub g() : Console.WriteLine("hello")
  Sub ToDo() : Throw New NotImplementedException()
  Shared Operator +(x As C, y As C) As Integer : Return x.i + y.i
  ReadOnly Property p As Integer : Return 5
  • Note: this prevents Proposal1, which is a bit of a shame! Unless you come up with the rule that "End Sub" is optional, like ; is in javascript.
Q. What if I put three statements? would I still need "end sub" ?
   Sub g2() : x=15 : y=17

  Function f() As Integer = 15
  Sub g() = Console.WriteLine("hello")
  Shared Operator +(x As C, y As C) As Integer = x.i + y.i
  ReadOnly Property p As Integer = 5  ' doesn't work

  Function f() As Integer := 15
  Sub g() := Console.WriteLine("hello")
  Shared Operator +(x As C, y As C) As Integer := x.i + y.i
  ReadOnly Property p As Integer := 5

  Function f() As Integer 15
  Sub g() Console.WriteLine("hello")
  Shared Operator +(x As C, y As C) As Integer x.i + y.i
  ReadOnly Property p As Integer 5

  Function f() As Integer Return 15
  Sub g() Console.WriteLine("hello")
  Shared Operator +(x As C, y As C) As Integer Return x.i + y.i
  ReadOnly Property p As Integer Return 5
Proposal 3: Only solve it for autoprops, but use a new keyword:
  Computed Property p As Integer = x
  • This looks confusing! It looks more complicated than what it's actually doing.
  • REJECTED proposal 3.
VB already has
   Property p As Integer = GetFoo() + 17  ' this is done at initialization-time
RESOLUTION: We're proud not to do anything. None of the proposals buy that much, and none are that special.

41. Primary constructors.

  • Rejected for VB. Would have had parity with C# vNext feature. *
The notes are offline. Let me (lwischik) know if you'd like me to type them up.

43. Literal type suffixes for Byte and SByte.

  • Approved.*
Dim x = 15L
Dim x = &HFACEL
Literals are NOT target-typed in VB. They are integers (unless with type suffix).

Byte is the only numeric type in VB which lacks a suffix. The reason it lacks it is because the obvious choice "B" is already taken by hex digit.

Byte is unusual - it's the only numeric type where the default is "unsigned". e.g. "Integer" means "Signed Integer" but "Byte" means "Unsigned Byte".

RESOLUTION: yes. Use "SB" and "UB". That will avoid the confusion of the previous paragraph. (other contenders were "Y and SY" like F#, or "Y and UY").

Q. Can you write "Dim x As Integer = &H00000000000000000000FACE ?
Likewise can you write "Dim x = &H0FUB"

Q. Can you have extra "1"s? e.g. Dim x As Signed Byte = &B111111111111110 SB ? The point is that leading 1s are not significant for signed binaries.

Suspect the current rule is "any leading bits you specify had better be zero". That's a decent enough rule. We could get even more strict ("don't have more leading zeros") but that would be inconsistent.

This feature was triggered by a request of MVP Bill McCarthy, who wrote:

So If I want to force a binary number to Int16, I can use the type suffix:
   Dim i As Int16 = &B 1000 0000 0000 0000S
Without the S this would fail as the literal would be an overflow for Int16. First off, I think it would be nice if the S could be spaced, eg:
   Dim i As Int16 = &B 1000 0000 0000 0000 S
And it'd be nice if there was an error correction suggestion that suggested the S suffix.

And finally, for signed bytes, could we perhaps have a SB suffix, eg:
   Const  b as SByte = &B 1000 0000 SB
The following workaround just doesn't do it for me:
   Const b As SByte = CSByte(&B 1111 1111 1000 0000S)
MVP Cory Smith wrote:
I do agree that if you specify the SByte, it should attempt to do the appropriate conversion without requiring the following:
   Dim s As SByte = CSByte(&B00010001)
Especially if this works:
   Dim b As Byte = &B00010001
Here’s another option:
   Dim s As SByte = 1
This works as well, so shouldn't any string literal work? I'm just guessing that this was missed somewhere along the way and is an unintended bug in the current implementation. My expectation would be that the implicit conversions for any literal would work like any other literal whether it is a number or a &B.
Mar 29, 2014 at 1:03 AM

44. Dictionary member initializers

  • Under discussion. Parity with C# vNext feature.
Currently VB lets you write dictionary-lookup expressions
   Dim x = y!name   ’ becomes this C# thing y[“name”]
C# is considering the following:
var expr = new Exception("oops");
var x = new JSON{
     $foo = 15,
     ["bar"] = 17,
     [expr] = 9 }
Proposal is to allow such things in VB object initializers. Here are possibilities...
1: Dim x = New C With { .member = "hello" } ' existing members
2: Dim x = New C With { !name = “hello” }  ' dictionaries

3: Dim x = New C With { (expr) = "hello" } ' dictionaries
4: Dim x = New C With { !(expr) = "hello" } ' a bit goofy? only consider this if second form turns out ambiguous
5: Dim x = New C With { .(expr) = "hello" }

6: Dim x = New C With { .Item(3) = "hello" } ' It's what you'd write if it were in a With block.

Dim $temp = new C
$temp("name") = ...
$temp(expr) = ...
Option 4: ! means default-property-access always. We could backport ! to generally mean "default property access" everywhere, including expressions and With blocks.

CON4: !x=3 and !(x)=3 now mean different things

PRO: People ask for a way to indicate indexing rather than invocation. They've often asked for []

Option 3: we currently have a.member, a!name, a(index) so everything that comes after the "a" is valid also inside the initializer. NB. requires the thing to be an LVALUE, i.e. no xml-members, no invocation.

CON: Can't use this in a With block on the right hand side, e.g. "With d : Dim a = .y : Dim b = (z) : End With"

Option 5: We are omitting the default property name.

RESOLUTION: We like 1, 2 and 4.
RESOLUTION: 6 is also fine, for uniformity, but is lower priority.

Q. Should we allow .$ as an alias for ! in VB?
A. No. Let's leave it to the intellisense to help people find it

46. Dictionary literals

  • Haven't got to this. Don't think it's worth it.*
Currently VB has excellent support for array literals. But people want dictionary literals, and are forced to use anonymous-types with reflection. For instance, they write
   InitializeMvc(New With { .arg1=“hello”, .arg2=“world”})
   Dim s = “hello \{name}”.Format(New With {.name=“fred”})
The first example is a typical MVC idiom, where anonymous types are used instead of named arguments, for cases where named arguments aren’t flexible enough. The second example is similar how Python does string interpolation. Both cases require reflection to get at the members of the anonymous object, which is a pain.

Look at how array literals work...
Sub f(Of T)(x As IEnumerable(Of T))
f( {1,2,3} ) ' today this picks up Integer as a hint for T
Proposal is to allow a new form of dictionary literal:
   Dim x = New With { arg1 := “hello”, arg2 := “world”}
Typical scenario is, as above, where the keys of the dictionary are strings that should be compared with the normal InvariantCulture comparison (since they’re used like parameter names rather than human-language strings). It’s not clear however what the type of the dictionary should be, and how it will work with type inference. For instance,
   Sub f(x As Dictionary(Of String, Object))
   Dim y = New With { arg1 := 1, arg2 := 2 }
   f(New With { arg1 := 1, arg2 := 2 })
We’d expect “y” to be inferred as Dictionary(Of String,Integer) or IDictionary or ReadOnlyDictionary or something like that. However, we’d expect the call to “f” to be compatible with the parameter type.

Here are some sketched syntaxes to see if they look any good...
Dim x = New With { arg1 := "hello" }
Dim x = { arg1 := "hello" }
Dim x = { (1+2) := "hello" }
Dim x = { arg1 = "hello" }
With fred
   Dim x = { !arg1 = "hello" }
End With

InitializeMvc( { !arg1 := "hello", … } )
Q. If we have dictionary literals, what would they generate?

A. If type context implements IDictionary<U,V> then use U for key and V for value, otherwise infer from dominant type of expressions - all like we do with array literals.

Q. What is the compile-time type of the dictionary-literal expression?
A. IDictionary<U,V>

Q. What is the run-time type of it?
A. Dictionary<U,V>

46. Fix the ternary If operator

  • Under consideration.
The basic problem is that when people see a ternary If operator with a type context
   Dim x As Integer? = If(True, 0, Nothing)
then they expect it to mean this:
   Dim x As Integer? : If True Then x = 0 Else x = Nothing
Proposal is that we should fix this for them as follows. When interpreting If(x,y,z) in a context in which the desired type is known, then interpret both y and z in the context of that type.

Note that it will be a breaking change. We decided in LDMs in years past that this would nevertheless be good for the language.

We must seek wider input on what people think about this. Once Roslyn goes open-source, it'll be great to open up to general public feedback.

47. More nullable support

  • Approved. VB-specific. Completes the nullable story.
Dim y = CInt?(5)
The issue is about Intrinsic nullable conversion operators (e.g. CInt?(<expression>))

Should we add them? If so, what does CInt?(5) mean?

Proposal 1: If(<a string> Is Nothing, Nothing, CInt(<a string>)
Proposal 2: If(Integer.TryParse(<a string>, result), result, Nothing)?
Proposal 3: CType(s, Integer?)

RESOLUTION: Yes, proposal 3.
Mar 29, 2014 at 1:25 AM

50. Allow As clause on Select clause range variables.

  • Under consideration. VB-specific.
Proposal: allow an “As Clause” on query Select clause range variable declarations. Maybe also on Anonymous-type member declarations too.

For example,
Dim x = From y As String In list
               Let q As Integer = foo()
               Select x As String = foo()
Currently you can specify "As" in Let clause, and it does an implicit conversion.

PROPOSAL: allow you to specify "As" in Select when introducing a new variable ("range variable") with an initializer.

Q. Can you use it after Aggregate? after GroupBy?

RESOLUTION: Anthony will flesh out the other LINQ places where As would go.

51. More implicit line continuations

  • Still under design. VB-specific.
Proposal: allow implicit line-continuations in these places:
  • Before “Then” in If … Then statements
  • Before “Implements” and “Handles” clauses
  • Others?
Here are some examples...
If x
Then Return 15

If x
   Return 15
End If

If x AndAlso
   y AndAlso
  Return 15
End If
We generally like the implicit LC before the Then token. The prettylister would put "Then" aligned with the "If" and "EndIf". But if you omit the Then token (an omission only allowed with multiline ifs), then what? ...? Needs more investigation.

52. Allow Handles to be combined with Implements

  • Not yet considered

53. Negative binary literals

  • Not yet considered
Proposal: If the type of the literal is a signed and has all bits explicitly specified we can create a negative number just like we do for hex literals today so
&B 1000 0000 SB = -128

54. ?. operator, which does null-check before proceeding

  • Approved, but needs more design-work
We want to write
  Dim x = customer?.Name
which would mean something like
   Dim x As String = If(customer Is Nothing, Nothing, customer.Name)
except it would put "customer" into a temporary variable (to avoid evaluating it twice, and to avoid race conditions)

This is a huge topic. I will write up a separate set of notes. There is big debate about what happens if you have several things in sequence, e.g.
Dim x = customer?.Name.Length
Proposal1: If you used ?. somewhere in the sequence, then you MUST continue to use it for every subsequent dot, otherwise it's a compile-time error. This boils down to ?. being a LEFT-ASSOCIATIVE operator, with everything that entails.

Proposal2: In the above example, you are allowed to write "Name.Length", with the intended meaning that you the programmer KNOW that if customer is non-null then (because of invariants in your code) you know that customer.Name is also non-null, and you wish to avoid the runtime cost of doing a null-check on it. This boils down to ?. being a RIGHT-ASSOCIATIVE operator, with everything that entails.

We also in VB expect to allow x?(y) which does a null-check on x prior to indexing or invoking it. (C# would only x?[y] or x?.Invoke() -- it would not allow x?() for conditionally invoking delegates).

Q. Which language operators should have "null-checking" versions of them? We've already seen the null-checking member-access operator x?.y and the null-checking indexing-or-invoking operator x?(y). Are there any others?

We had wondered if there is ever need for a null-checking conversion operator, i.e. one that does a null-check prior to doing the conversion. Normally it's not needed, since users who write their own user-defined-conversions can roll the null-check into those. So the question is whether it's needed for intrinsic conversions. The only intrinsic conversion that might benefit from a null-check is Integer? -> String. This is the only intrinsic conversion which can throw a NullReferenceException.

PROPOSAL 1: Allow null-checking conversion operator, for this one case.

PROPOSAL 2: Change behavior of Integer? -> String intrinsic conversion, so it gives you an empty string if the input is null.

RESOLUTION: Rejected proposal 1 because it's too niche. Proposal 2 is decent, but low priority.
Apr 4, 2014 at 5:16 PM
Re: 46. Dictionary literals

Instead of a Dictionary literal, I think it could be better to implement a Key-Value Pair literal instead. (Think JSON)
A Key-Value Pair Literal { "A" : 1 }
This re utilises the Expression / Statement Continuation Character in and Array Literals.

In C# : is only used in (as far I can tell)
 cond ? trueExpr : falseExpr  ' Inline IF
 class B : A                  ' Inheritance
both of which are contextually different so I can't see another contextual definition being a problem.

This would in essence make a Dictionary Literal just a collection of Key-Value Pairs. Eg
Dim myDict = { { "A" : 1 } , {"B" : 2 } , { "C" : 3 } }
Is then translated into the following code.
Dim myDict As New Dictionary(Of String, Integer)(  { ( "A" : 1 ) , ( "B" : 2 ) , ( "C" : 3 ) } 
This then only needs a overload for the constructor that takes an IEnumerable(Of IKeyValuePair(Of K, V)).

The compiler could also check at compile-time that the dictionary initialisation is valid. Eg Are the Keys Unique.
May 14, 2014 at 9:51 PM
Allow With on the Case Else clause
would permit the following style of coding
            Select Case flags 
              Case CharFlags.White       : ' stay in FollowingWhite
              Case CharFlags.CR          : state = AccumulatorState.CR
              Case CharFlags.LF          : state = AccumulatorState.Done
                                           terminatorLength = 1
                                           Exit While
              Case Else When (flags And (CharFlags.Complex Or CharFlags.IdentOnly)) <> 0
                                         : state = AccumulatorState.Bad
                                           Exit While
              Case Else                  : state = AccumulatorState.Done
                                           Exit While
            End Select
rather than current (less elegant work around)
            Select Case flags 
              Case CharFlags.White       : ' stay in FollowingWhite
              Case CharFlags.CR          : state = AccumulatorState.CR
              Case CharFlags.LF          : state = AccumulatorState.Done
                                           terminatorLength = 1
                                           Exit While
              Case Else
                 If (flags And (CharFlags.Complex Or CharFlags.IdentOnly)) <> 0 Then
                    state = AccumulatorState.Bad
                    Exit While
                 End If
                 state = AccumulatorState.Done
                 Exit While
            End Select
Also allow the When to the preceded with an implicit line continuation. So that if can be placed / aligned underneath the relevant Case clause.
This will improve the readability and formatting.
              Select Case baseCorTypeId 
                Case SpecialType.System_Enum                                   :  result = TypeKind.Enum ' Enum
                Case SpecialType.System_MulticastDelegate                      :  result = TypeKind.Delegate   ' Delegate
                Case SpecialType.System_Delegate
                  When Me.SpecialType <> SpecialType.System_MulticastDelegate  :  result = TypeKind.Delegate ' Delegate
                Case SpecialType.System_ValueType
                   When Me.SpecialType <> SpecialType.System_Enum              :  result = TypeKind.Structure ' Struct 
                Case baseCorTypeId
                    When Me.Arity = 0 AndAlso
                         Me.ContainingType Is Nothing AndAlso
                         ContainingPEModule.Module.HasAttribute(Me.m_Handle, AttributeDescription.StandardModuleAttribute)
                                                                               :  result = TypeKind.Module
              End Select 
Note: The code is from my roslyn fork
May 17, 2014 at 1:51 PM
The When clause should bind to each caseClause and not the entire collection of caseClause of that caseStatement

 Case  caseClause_1 , caseClause_2   When BoolExpr   :  caseActionExpr
      | caseClause | | caseClause | | whenClause  |    |              |
      +------------+ +------------+-+-------------+    +--------------+
                     | caseClauseWithCondition    |
| caseStatement                                                        |
Usage example
Select Case x
  Case Is < 0  : Throw someException
  Case 0 When a_Cond,
       1 When b_Cond     : Return "Result A" 
  Case 2 When a_Cond     : Return "Result B"
  Case Else When b_Cond  : Return "Result C"
  Case Else
    Return "Other"
End Select

End Select
would be functionally equivalent to the following If ... ElseIf ... Else ... End If construct
If ( x = 0 ) Then
  Throw SomeException
ElseIf ( (x = 0) AndAlso ( a_cond ) ) OrElse
       ( (x = 1) AndAlso ( b_cond ) ) Then
  Return "Result A"
ElseIf ( (x = 0) AndAlso ( a_cond ) ) Then
  Return "Result B"
  If ( b_cond ) Then
    Return "Result C"
    Return "Other"
  End If
End If