Vampire
10/06/2026, 5:39 PMVariableExpression.THIS_EXPRESSION the STC and thus static-compile gets confused.
We use this at some places in the Spock transform.
While trying to make Spock specs compatible with STC and SC, it was observed that STC stores type information at the current node and using VariableExpression.THIS_EXPRESSION then causes conflicting casts when used inside and outside a closure.
As a work-around we can instead use new VariableExpression("this"), but is this expected, or should it work properly and I should report a bug?
As an example.
class ASpec extends Specification {
def "mock init closure"() {
given:
List<String> list = Mock(List) {
size() >> 3
}
when:
int size = list.size()
then:
size == 3
}
}
compiles in semantic analysis to
import spock.lang.*
@org.spockframework.runtime.model.SpecMetadata(filename = 'script.groovy', line = 1)
public class ASpec extends spock.lang.Specification {
@org.spockframework.runtime.model.FeatureMetadata(name = 'mock init closure', ordinal = 0, line = 2, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHEN, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = [])], parameterNames = [])
public void $spock_feature_0_0() {
org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 0)
java.util.List<String> list = org.spockframework.runtime.SpecInternals.MockImpl(this, 'list', java.util.List, java.util.List, { ->
this.getSpecificationContext().getMockController().addInteraction(new org.spockframework.mock.runtime.InteractionBuilder(5, 7, 'size() >> 3').addEqualTarget(it).addEqualMethodName('size').setArgListKind(true, false).addConstantResponse(3).build())
})
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 1)
java.lang.Integer size = list.size()
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 1)
org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 2)
try {
org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'size == 3', 12, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), size) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 3)))
}
catch (java.lang.Throwable $spock_condition_throwable) {
org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'size == 3', 12, 5, null, $spock_condition_throwable)}
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 2)
this.getSpecificationContext().getMockController().leaveScope()
}
}
The important part here is the two getSpecificationContext() calls, one inside the mock init closure, one as last line in the feature method, both added by the Spock transform.
If those use VariableExpression._THIS_EXPRESSION_ then at runtime you get java.lang.ClassCastException: apackage.ASpec cannot be cast to apackage.ASpec$__spock_feature_0_0_closure1.
If you use new VariableExpression("this") instead, it works as intended.Vampire
10/08/2026, 10:29 PM