The question mark in the title is only there because I'm very reluctant to call anything a compiler bug, but in this case, I'd be surprised if anyone could explain this behavior in any other way.
The code to reproduce the problem is very, very simple. In a standard module we have the following:
Sub CompilerBug()
Dim oClass As cClass
Set oClass = New cClass
If False Then
Debug.Print "This doesn't print, as it shouldn't."
End If
If Falsee(oClass.Clone) Then
Debug.Print "This does print, although it shouldn't!"
End If
End Sub
Public Function Falsee(oClass As cClass) As Boolean
Falsee = False
End Function
And we have a class (cClass
) defined in a class module named cClass, containing the following code:
Public Function Clone() As cClass
Dim oClass As cClass
Set oClass = New cClass
Set Clone = oClass
End Function
Private Sub Class_Terminate()
End Sub
The code is pretty self-explanatory, the second if statement gets entered in spite of the aptly named function Falsee
returning False
, no matter the input! The same result can be observed when the function in the class is replaced by a similar Public Property Get
.
For the purpose of reproduction, I'm getting this behavior in my Office 365 Excel, 64bit, Version 2011 (Build 13426.20274) which is the current up-to-date version of excel. I also tested this exact code in my Word VBA IDE with exactly the same results.
I have no idea what causes this behavior, but here are a few clues:
If we rewrite the code in our sub to:
Sub CompilerBug()
Dim oClass As cClass
Set oClass = New cClass
Dim bFalse As Boolean
bFalse = Falsee(oClass.Clone)
If bFalse Then
Debug.Print "This doesn't print, as it shouldn't."
End If
End Sub
(First if statement omitted for the sake of brevity) The code gets executed as expected, so it is crucial that the function is called directly in the condition for the if statement (not something that should usually make a difference)
And the next interesting clue is the following (assume we use the buggy sub code again with If Falsee(oClass.Clone) Then
): If we remove the following from our class module:
Private Sub Class_Terminate()
End Sub
The if statement works as expected and nothing gets printed! So somehow the Terminate event being executed during evaluation of the If-statement messes things up, but the Class_Terminate()
sub doesn't even contain any code! That's the next thing that shouldn't make a difference, yet does! This idea is further supported by the fact, that when we declare a public variable in the module by adding Public poClass As cClass
at the top and rewrite the function code to:
Public Function Falsee(oClass As cClass) As Boolean
Set poClass = oClass
Falsee = False
End Function
Now the Terminate event doesn't get called during execution of the If-statement, because the instance of the class doesn't go out of scope during the execution of the If-statement and as a result, the If-statement evaluates properly - the Line doesn't get printed.
Obviously, the Terminate-event being executed during evaluation of the If-statement can't be the whole story, because this happens all the time. It also seems to have something to do with the scope of the object that gets terminated and the way the parameter is passed to the function. For instance, the following does NOT produce the same behavior:
Module code:
Sub CompilerBug()
Dim oClass As cClass
Set oClass = New cClass
If Falsee(oClass.CreateAndDestroyObject) Then
Debug.Print "This doesn't print, as it shouldn't."
End If
End Sub
Public Function Falsee(lng As Variant) As Boolean
Falsee = False
End Function
And in the class module:
Public Function CreateAndDestroyObject() As Long
Dim oClass2 As cClass
Set oClass2 = New cClass
Set oClass2 = Nothing
End Function
Private Sub Class_Terminate()
End Sub
To sum everything up, the behavior occurs when:
A method of a class returns an instance of the same class, and this method is called inside an If-statement as an argument for a function, and this instance of the class (that was created by the method) then goes out of scope inside that function and the terminate event of the class gets called (and exists as code). In this case, the if statement gets entered, regardless of the return value of the function.
To me, many questions remain... Why does the Terminate event make a difference in this case? Is any of my code supposed to produce undefined behavior or is this actually a bug? Are there other cases where If-statements don't work the expected way? What exactly causes this bug?
Aucun commentaire:
Enregistrer un commentaire