A Roslyn bug? On non-shared member, I'm getting error that I'm using 'shared member initializer' - vb.net

Have the following trivial code:
Class A
Private value As Integer = 1
Sub Action(Optional param1 As Integer = value)
End Sub
End Class
Visual Studio complains about default value (value) with error BC30369:
Cannot refer to an instance member of a class from within a shared method or shared member initializer without an explicit instance of the class.
Is this really the right error for this case? The method is not shared.
In Visual Studio 2012 or 2013, the error in the same case is
Constant expression is required.
what absolutely makes sense.

After additional research I think that there is a problem in order of checks made by compiler.
If I change the code, making value member Shared, I get correct result: Error BC30059
Constant expression is required.
Since nothing except constants can be placed into default value of Optional clause, check for the above BC30059 ("Constant expression is required.") should be obviously made "earlier" than the check for BC30369 (shown in question).
I created bug report at Microsoft Connect.

Related

Why are missing `Set` for certain types a runtime error rather than compile error?

Given the following VBA code, assuming that a Something is just a VBA class module....
Public Type Foo
SomeThing As Something
End Type
Public Sub TestFoo()
Dim x As Foo
With x
'Correct way to do it
Set .someThing = New Something
End With
With x
'This is wrong but realized only as a RTE
'438: Object doesn't support this property or method
.SomeThing = New Something
End With
End Sub
In contrast, if you change the type to something like VBA.Collection as below:
Public Type Foo
SomeThing As VBA.Collection
End Type
Public Sub TestFoo()
Dim x As Foo
With x
.SomeThing = New VBA.Collection
End With
End Sub
This is now a compile error, with Argument Not Optional. This is obviously wrong but why is it a compile-time error only with VBA.Collection?
This is explained in the VBA Language Specification. The semantics of the assignment inside the With block is driven by whether or not the statement is a Set statement or a Let statement. In this context, it is a Let statement:
With x
.SomeThing = New Something
End With
Note that in the VBA grammar, the keyword Let is optional (obsolete):
let-statement = ["Let"] l-expression "=" expression
In a Set statement, the Set keyword is required:
set-statement = "Set" l-expression "=" expression
Inside of a With block, the l-expression is basically the UDT member, although the exact same behavior applies if x is used directly.
When evaluating a Let expression, the semantics are described in section 5.4.3.8:
Static Semantics.
This statement is invalid if any of the following is true:
<expression> cannot be evaluated to a simple data value (section 5.6.2.2 ).
Following that to 5.6.2.2 (Evaluation to a simple data value), the following runtime semantics apply (applicable rule only):
Runtime semantics.
At runtime, the simple data value’s value and value type are
determined based on the classification of the expression, as follows:
If the expression’s value type is a specific class:
If the source object has a public default Property Get or a public
default function, and this default member’s parameter list is
compatible with an argument list containing 0 parameters, the simple
data value’s value is the result of evaluating this default member as
a simple data value.
Otherwise, if the source object does not have a public default
Property Get or a public default function, runtime error 438 (Object
doesn’t support this property or method) is raised.
Thus the runtime error 438 for SomeThing As Something.
With the Collection, the Let static semantics still apply, but it fails the static semantics of 5.6.2.2 (which gives the compile error). Again, the preceding inapplicable semantics are omitted:
Static semantics. The following types of expressions can be evaluated to produce a simple data value:
An expression classified as a value expression may be evaluated as a simple data value based on the following rules:
If the declared type of the expression is a specific class:
If this class has a public default Property Get or function and this default member’s parameter list is compatible with an argument
list containing 0 parameters, simple data value evaluation restarts as
if this default member was the expression.
The default member of Collection is a function (.Item) that takes a single parameter Index. In this code, the parameter is not provided, so the argument list is incompatible:
With x
.SomeThing = New VBA.Collection
End With
Thus the Argument Not Optional compile error.
In typing up the question, it struck me that the VBA.Collection had a default member. Thus, the compiler was interpreting the .Something = New VBA.Collection as an assignment to the default member... except that the Item is an indexed property. That explains why we get Argument not optional which would be quite a strange error to get with a Set statement.
In contrast, a VBA class module might not have a default member at all, so that there is no indexed property + default member to trigger a compile-time error. However, it also means that the bad syntax won't be caught until runtime.

How serious is BC42020 in an upgraded VB .Net project?

Consider the following line of code which used to compile without warnings.
Public SetUpDone = False
After upgrading a project to Visual Studio 2017 from Visual Studio 2005 over a hundred of these BC42020 warnings exist. The MSDN description of the warning simply states that the variable defaults to type object. I don't have a good idea of the seriousness of this type of warning. The debugger indicates that the code executes as I expect. Is it merely a performance type of issue?
Secondly, I thought that Visual Basic supported some form of Type Inference so I'm not clear about why it wouldn't be able to deduce that the type should be Bool.
Another example is the following where the function returns a String
Dim dayTxt = " " & GetTextFromIni("General", "Var50")
I would have thought that type inference would work here and deduce that dayTxt is a String.
This:
Public SetUpDone = False
Is equivalent to this:
Public SetUpDone As Object = False
As suggested, type inference is only for local variables, not fields. With Option Infer On, this inside a method:
Dim SetUpDone = False
would indeed be equivalent to this:
Dim SetUpDone As Boolean = False
There are a couple of issues with the code as you have it. Firstly, it means that every use of that False value requires unboxing which makes your code slower. That's the case for any value types, i.e. structures. Value types are normally stored on the stack but, when boxed, are stored on the heap.
Secondly, it means that any member access will require late binding. That's not an issue for Boolean values because they have no members of interest anyway but if it was, say, a DateTime then the IDE would never provide Intellisense for that type because al it would see would be type Object and the existence of the specified member would have to be confirmed at run time, making the code less efficient again.
Thirdly, it means that the compiler can never confirm that you're passing the correct type as a method argument. For instance, if you have a method with a Boolean parameter, the compiler won't know that you're passing a Boolean if you pass that field because it's type Object. That also means that if you pass some other Object variable that doesn't contain a Boolean, the compiler can't warn you.
As suggested, you should turn Option Strict On in the project properties. That will flag every instance of you're not specifying the appropriate type for something. Fixing those errors will, at the very least, make your code a bit more efficient. It may even draw your attention to situations where exceptions will or could be thrown at run time. Having Option Strict On enforces strict typing so it makes you think more about the types you're using. Even if you're conscientious about that with Option Strict Off, you can still make mistakes that Option Strict On will prevent.

Why doesn't VB.Net type inference work in class fields?

If I were to type the following into a method body:
Dim myInt = 1
the Visual Studio IDE (and therefore, I am guessing, the compiler) infers the type of myInt to be Integer.
EDIT
Apparently using a literal was a bad choice here, since I've become embroiled in a lengthy debate that has nothing to do with the question. If you take issue with the fact that the expression 1 might be interpreted as an instance of different numeric types, pretend I had written:
Dim myInstance = New MyClass()
END EDIT
However, when I put a field declaration with the exact same code at the top of a class, the type of myList is not inferred:
Public Class Foo
Dim myInt = 1
End Class
On mouseover, it mentions the absence of an As clause, and says a type of Object has been assumed. I cannot pass myInt as an argument to a function or sub that expects an Integer argument, without explicitly adding an As clause or casting to Integer.
Is there a discrepancy between how the IDE and compiler deal with type inference? If, on the other hand, the compiler can't infer type in this situation either, why the discrepancy between method variables and class fields?
What you've found is that way on purpose. here is the MSDN expalanation.
Local type inference applies at procedure level. It cannot be used to
declare variables at module level (within a class, structure, module,
or interface but not within a procedure or block). If num2 in the
previous example were a field of a class instead of a local variable
in a procedure, the declaration would cause an error with Option
Strict on, and would classify num2 as an Object with Option Strict
off. Similarly, local type inference does not apply to procedure level
variables declared as Static.

DateTime.Today Error

In VS2010 for a VB.NET 4.0 project, the IDE puts a green line under the last line in the following code:
Dim cityLocal As DateTime
cityLocal = externalFunction()
cityLocal.Today()
The suggested code replace is to update 'cityLocal' with 'Date'. The reason is:
Access of shared member, constant member, enum member or nested type through an instance; qualifying expression will not be evaluated.
But it does compile and does work correctly. Is this just a bug in the VS2010?
Today is a shared member, thus should not (but can) be accessed through an instance of DateTime change your code to.
DateTime.Today
Although Visual Studio gives you suggestions to correct the "Error" it is infact a compiler warning, warning you that there is no need for an instance to access the shared member. You'll find that it is not listed as an error in the error list. Which is why it compiles correctly.
The Visual Basic language specification states
9.2.4 Shared Methods
The Shared modifier indicates a method
is a shared method. A shared method
does not operate on a specific
instance of a type and may be invoked
directly from a type rather than
through a particular instance of a
type. It is valid, however, to use an
instance to qualify a shared method.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=01EEE123-F68C-4227-9274-97A13D7CB433:
More information on the warning can be found in the documentation.
http://msdn.microsoft.com/en-us/library/y6t76186.aspx
Date.Today is a static (Shared in VB.NET) property. You are able to use it from an instance because the compiler knows to make the proper call, but it is not the expected usage pattern, which is both unnecessary and undesirable to use directly from an instance.
As a static variable, you should use Date.Today rather than variable.Today.
Today is a shared/static member. Normally you would use DateTime.Today not your instance variable.
http://msdn.microsoft.com/en-us/library/system.datetime.today.aspx

In VB6, how do I call a COM object requiring a pointer to an object?

I'm having trouble with a .NET Assembly that is com visible, and calling certain methods from VB6.
What I have found is that if the parameters are well defined types, (e.g. string), calls work fine. If they are higher level objects, it raises a runtime error '438' suggesting that the property or method is not present. I suspect that this is a question of having the correct signature on the call, but I can't see how to do this correctly.
I believe that I've done everything correct on the .NET side (ComVisible, public interfaces, etc. and even have it down to a simple enough case).
Looking at the output from the typelib viewer, I have the following:
dispinterface ISimple {
properties:
methods:
[id(0x60020000)]
void Add([in] ISimpleMember* member);
[id(0x60020001)]
ISimpleMember* Create();
};
OK. So I have 2 methods in my ISimple interface. One takes an ISimpleMember (Add), whilst the other, returns an ISimpleMember.
The corresponding code in VB looks like this:
Dim item As ISimpleMember
Dim simple As simple
Set item = New SimpleMember
item.S1 = "Hello"
item.S2 = "World"
Set simple = New simple
simple.Add (item) <---- This raised the run time error 438
Set item = simple.Create <---- This works fine, returning me an ISimpleMember
I've tried a couple of things:
1. Dim item as SimpleMember (makes no difference)
2. simple.Add(ObjPtr(item)) - Syntax error
3. simple.Add(ByRef item) - Syntax error
Basically, The run time error is the same as if I had
simple.AMethodThatIHaventWritten()
Also, If I browse References in the VB6 Environment, The Add method is well defined:
Sub Add(member As SimpleMember)
I've found the answer I believe. It was very simple:
When calling a SubRoutine, I shouldn't put the name in braces. the call should have been:
simple.add member
rather than
simple.add(member)
If I change it to a function (i.e. return a value rather than void) the braces are necessary
This seems to work
(Probably) The top 3 VB6 coding mistakes made by devs who now mainly code in C#, Javascript etc. Are:-
Placing ; at the end of lines. Its a syntax error very easily spotted and picked up the compiler.
Not placing Then on the other side of an If condition expression. Again its a syntax error.
Calling a method without retrieving a value and yet using ( ) to enclose the parameter list. With multiple parameters this is a syntax error and easily found. With only one parameter the use of ( ) is interpreted as an expression. Its the result of the ( ) expression which is passed as parameter. This causes problems when ByRef is expected by the callee.