This project is read-only.

VB LDM 2014-03-12

Topics: VB Language Design
Apr 27, 2014 at 7:28 AM

ProtectedAndFriend, ProtectedOrFriend


The CLI includes two access modifiers "FamilyAndAssembly" (which allows access to derived types that are in the same assembly) and "FamilyOrAssembly" (which allows access to types if they are derived, and also allows access to types if they are in the same assembly). The concepts are explained well here:


VB and C# already have syntax for FamilyOrAssembly ("Protected Friend"). Please add syntax to allow VB to express FamilyAndAssembly.


VB will add two new access modifiers, which are exact transliterations of the CLI keywords...
The pretty-lister will not turn "Protected Friend" into "ProtectedOrFriend". However, if the user types "private protected" (or whatever syntax C# chooses) then it will pretty-list the entire file into C#. JOKE. No it won't. But it may pretty-list the C# syntax into "ProtectedAndFriend".

Here are some code samples:
   ProtectedAndFriend 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


Alex wrote, "Around Private Protected, I really feel like we make a mistake if we choose our VB syntax with the arbitrary choice that C# made so that it could look like C++. We should embrace this as one of those times where VB’s trend towards verbosity leads naturally to a design whose meaning is obvious at first glance. We don’t want to get stuck in a design rut where we’re OK with C# being clearer than VB where terseness pays off, but not vice versa where verbosity pays off – by definition, then, VB will always be less clear than C#."

Neal observed that the sense of "And/Or" is confusing. It might be a logical And of the permissions, or a logical And of the restrictions. Nevertheless, the CLI has chosen a canonical sense, and we're copying it.


private protected
protected if internal
internal && protected  // also "internal || protected"
internal and protected  // also "internal or protected"
internalAndProtected  // also "internalOrProtected"
ProtectedAndFriend  ' also "ProtectedOrFriend"
Protected And Friend  ' also "Protected Or Friend"
ProtectedFriend   ' rejected because it's just one space away from "Protected Friend"
FamilyAndAssembly  ' also "FamilyOrAssembly"
Private Protected
Protected OnlyIn Friend
Protected But Friend
Protected If Friend
Protected When Friend
Protected Xor Friend  ' just a joke... :)

Faster CInt(Double)


double d = 10.5;
int x = (int)d;
Dim x = CInt(d)
The VB version is slower than C#. Indeed, any time we call CInt, the compiler always codegens a call to Math.Round. Even CInt(Math.Round(x)) gets compiled into Math.Round(Math.Round(x)).


Please allow us to write fast integer casts in VB.


Part 1: Any time the compiler generates an implicit call to Math.Round (i.e. as part of CInt, CLong, CULong, CUint), and if the argument of that implicit call is one of a list of math functions known to return a whole number, then omit the outer call to Math.Round. The list of known math functions is: Round(d), Round(d,MidpointRounding), Truncate(d), Floor(d), Ceiling(d), VisualBasic.Fix(d), VisualBasic.Int(d).

Part 2: If the user writes CInt(Math.Truncate(d)) then we will emit conv.i4 or conv.ovf.i4 dependent on the current project-level "check-overflow" settings. (TODO: verify that conv.i4 has exact same semantics as CInt(Math.Truncate), including negative values, superlarge doubles and exceptions, NaN, Infinity). Also do likewise for conv.i1, conv.i2, conv.i4, conv.i8 and for their unsigned versions, and for both checked and unchecked versions.


This is a compiler optimization, pure and simple. It adds no new syntax or concepts or library functions. We identify targeted scenarios and generate optimal IL in those cases where semantics would not be affected. This seemed the cleanest approach. Specifically, it seemed better than adding any of the following syntaxes:
DirectCast(d, Integer)
d As Integer
Will it be better to special-case just one of these combinations? e.g. conv.i4 on CInt(Truncate(double))? Or is it better to just do all of them? Default answer: "yes do all of them", and only scale back if the compiler-dev comes back and says it's too hard.

There are other possible concerns, e.g. what happens if it turns out that the semantics of conv.i4 are actually different from ALL of the Math.Round-like functions? What if it turns out that it's really hard to implement every case? What if expression-trees throw a wrench in the works? Well, when devs set about implementing the feature, then we can come back to check.