diff --git a/docs/release_notes.adoc b/docs/release_notes.adoc index 4be8ecafb8..28a8ee2b28 100644 --- a/docs/release_notes.adoc +++ b/docs/release_notes.adoc @@ -8,6 +8,7 @@ include::include.adoc[] === Enhancements +* Add support for statically type-checked or compiled specifications: conditions, interactions, `old()`, and data tables referencing previous columns now work in specs or feature methods annotated with `@SpockTypeChecked` or `@SpockCompileStatic`, see <> spockPull:2397[] * Add support for `final` local variables in `where:` blocks, declared at their beginning and evaluated once per feature, scoped to the where-block spockIssue:138[] * Improve `TooManyInvocationsError` now reports unsatisfied interactions with argument mismatch details, making it easier to diagnose why invocations didn't match expected interactions spockPull:2315[] @@ -23,6 +24,10 @@ include::include.adoc[] === Breaking Changes * Mock/Stub checks on `Comparable` with `T` being something other than `Object` now compare using the java identity hash code instead of always being equal spockIssue:2352[] +* If you used `@TypeChecked`, `@CompileStatic`, or `@CompileDynamic` anywhere within a Spock specification, this will now fail compilation. + Use `@SpockTypeChecked`, `@SpockCompileStatic`, or `@SpockCompileDynamic` instead, which have the same semantics and effect during Groovy compilation. + On non-specification classes you use the normal built-in annotations. + For further information see <> spockPull:2397[] == 2.4 (2025-12-11) diff --git a/docs/spock_primer.adoc b/docs/spock_primer.adoc index 74d16d7e8a..06d7d1a99a 100644 --- a/docs/spock_primer.adoc +++ b/docs/spock_primer.adoc @@ -788,6 +788,67 @@ will be rendered as include::{snapshotdir}/primer/VerifyEachDocSpec/verifyEach_with_index_method.txt[] ---- +[[statically-type-checked-specifications]] +== Statically Type-Checked or Compiled Specifications + +Specifications can be compiled with Groovy's static type-checking or static compilation by annotating the specification +class or individual feature methods with `@SpockTypeChecked` or `@SpockCompileStatic` or by using a compiler configuration script. +All major features work under static type-checking and compilation: conditions including their failure rendering, +exception conditions, interactions and stubbing, `old()`, `with`, `verifyAll`, and data driven features. + +The advantage of static type-checking is, that you are forced to provide more explicit types or use the correct types +and not superclasses that only work due to Groovy's duck-typing. As a consequence also the IDE you use does have better +type information and can better to usage-searches, refactorings, and so on. + +The advantage of static compilation over static type-checking is, that additionally the compiled bytecode does not have +the Groovy dynamic magic in it and can thus execute the tests faster, so if you do not need the Groovy dynamic language +features, you can save some execution time. + +Since the static type checker analyzes the code after Spock has rewritten it, a few rules apply: + +* Specification fields need to be explicitly typed, except if they are only used as `Object`. + Spock separates the declaration from the initialization so that extensions can wrap around the field initialization. + Due to that, the type of the field cannot be inferred from the initializer expression. + With `def list = []` the field, as far as the static type-checker is concerned, is typed as `Object`, + so calls on it will not pass type checking. +* Data variables in `where:` blocks default to `Object`. Declare typed feature method parameters to use them + with methods or operators that need the actual type, e.g. `def "maximum of two numbers"(int a, int b, int c)`. + Only those where the type needs to be known need to be declared explicitly. +* Some closures like the preconditions in `@IgnoreIf` and `@Requires`, or some interaction result calculation closures, + or argument constraint closures might need explicit typing for the closure argument to compile properly, + e.g. `@IgnoreIf({ PreconditionContext it -> it.os.windows })`. +* Some more places like variables initialized with a not-casted `null` might need explict type declaration. +* Private methods and fields cannot be simply accessed anymore, but need to be referenced using method closure syntax (`&.`) + or field accessor syntax (`@.`) or if really the property needs to be accessed a method closure syntax for the accessor method. + +Some limitations remain: + +* Global Groovy mocks (e.g. `GroovySpy(Foo, global: true)`) rely on Groovy's meta-object protocol and therefore + cannot intercept calls made from statically compiled code. The real method is called instead. + This is independent from whether the tests are statically compiled, but depends on whether the production code is. +* Dynamic language features, like dynamic method names or `methodMissing`, are not available, as in any + statically compiled Groovy code. +* Condition rendering can differ in minor ways. For example, the target class of a static method call like + `Math.max(a, b)` is not rendered under static compilation, because statically compiled code never evaluates the + receiver of a static method call, so there is no value to record. +* The standard Groovy `@TypeChecked`, `@CompileStatic`, and `@CompileDynamic` annotations must not be used anywhere within a Spock specification. + IDEs interpret these annotations, but use the code as written to display type errors, instead of the code as + rewritten by Spock which is what the annotations do in actual Groovy compilation. + This causes a lot of errors displayed that actually are no errors after Spock transformation, and on the other hand + miss to display a lot of errors that are errors after Spock transformation. + Due to that it is prohibited that you use those annotations. + Instead you use `@SpockTypeChecked`, `@SpockCompileStatic`, and `@SpockCompileDynamic`, which have the same semantics and effect but do not confuse IDEs. + On non-specification classes you use the normal built-in annotations. + Using a compiler configuration script with the standard annotations + works as intended as the IDE does not recognize those settings even if the configuration script is configured in the IDE. + The type-errors you then get from actual Groovy compilation of the specifications. +* The `@Use` extension with wich you can enable Groovy categories within the scope of a feature method or spec has no + effect. It would at runtime add those categories but the static type-checker cannot know that and thus does not see + the methods added by the category. With an explicit `use(...) { ... }` the static type-checker would even fail + explicitly due to that. As an alternative, you can make the category a Groovy extension module. As those are always + there at configuration, static type-checker can work with those. Or you can switch off the type-checking for where you + want to use `@Use`. + [[specifications-as-documentation]] == Specifications as Documentation diff --git a/settings.gradle b/settings.gradle index ef8f253799..d990cccf06 100644 --- a/settings.gradle +++ b/settings.gradle @@ -96,7 +96,7 @@ include "spock-junit4" include "spock-testkit" def variant = System.getProperty("variant") as BigDecimal ?: 2.5 -def javaVersion = (System.getProperty("javaVersion") ?: 8) as int +def javaVersion = (System.getProperty("javaVersion") ?: (variant == 5.0) ? 11 : 8) as int if (variant == 2.5) { //Remove once Groovy 2.5 support is dropped diff --git a/spock-core/src/main/groovy/spock/util/concurrent/BlockingVariables.groovy b/spock-core/src/main/groovy/spock/util/concurrent/BlockingVariables.groovy index ccccd66294..cd78307fdd 100644 --- a/spock-core/src/main/groovy/spock/util/concurrent/BlockingVariables.groovy +++ b/spock-core/src/main/groovy/spock/util/concurrent/BlockingVariables.groovy @@ -14,17 +14,25 @@ package spock.util.concurrent +import java.lang.reflect.Proxy import java.util.concurrent.TimeUnit import org.spockframework.util.ThreadSafe import org.spockframework.util.TimeUtil +import static org.spockframework.util.ObjectUtil.uncheckedCast + /** * Provides an unlimited number of dynamic variables. Reading the value of a * variable will block until some other thread has set the value of the variable, * or a timeout expires. Useful for verifying state in an expect- or then-block * that has been captured in some other thread. * + *

WARNING: That this class implements {@code Map} is solely for the purpose to use the + * same syntax under dynamic compilation, type-checked and static compilation. All {@code Map} methods + * except {@code get} with {@code String} as first argument and {@code put} will throw + * {@code UnsupportedOperationException}. + * *

Example: * *

@@ -53,9 +61,15 @@ import org.spockframework.util.TimeUtil
  * @author Peter Niederwieser
  */
 @ThreadSafe
-class BlockingVariables {
+class BlockingVariables implements Map {
   private final BlockingVariablesImpl impl
 
+  @Delegate
+  private final Map delegate = uncheckedCast(
+    Proxy.newProxyInstance(BlockingVariables.classLoader, [Map] as Class[]) { proxy, method, args ->
+      throw new UnsupportedOperationException(method.name)
+    })
+
   /**
    * Same as BlockingVariables(1, TimeUnit.SECONDS).
    */
@@ -109,4 +123,34 @@ class BlockingVariables {
   Object getProperty(String name) {
     impl.get(name)
   }
+
+  /**
+   * Sets a variable's value. This method should not be called directly
+   * but by setting a dynamic variable's value.
+   *
+   * @param name the variable's name
+   * @param value the variable's value
+   */
+  @Override
+  Object put(String name, Object value) {
+    setProperty(name, value)
+    null
+  }
+
+  /**
+   * Gets a variable's value. Blocks until a value has been set for the variable
+   * or a timeout expires. This method should not be called directly but by
+   * getting a dynamic variable's value.
+   *
+   * @param name the variable's name
+   *
+   * @return the variable's value
+   */
+  @Override
+  Object get(Object name) {
+    if (!(name instanceof String)) {
+      throw new UnsupportedOperationException("get")
+    }
+    getProperty(name as String)
+  }
 }
diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
index c71f3c986e..bf05daa3eb 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
@@ -16,17 +16,18 @@
 
 package org.spockframework.compiler;
 
+import groovy.transform.CompileDynamic;
+import groovy.transform.CompileStatic;
+import groovy.transform.TypeChecked;
 import org.codehaus.groovy.runtime.wrappers.PojoWrapper;
 import org.spockframework.mock.runtime.MockController;
 import org.spockframework.runtime.*;
 import org.spockframework.runtime.extension.RepeatedExtensionAnnotations;
 import org.spockframework.runtime.model.*;
 import org.spockframework.util.Identifiers;
-import spock.lang.Specification;
+import spock.lang.*;
 
 import org.codehaus.groovy.ast.*;
-import spock.lang.Verify;
-import spock.lang.VerifyAll;
 
 import java.util.Set;
 
@@ -49,6 +50,12 @@ public class AstNodeCache {
   public final ClassNode DataVariableMultiplicationFactor = ClassHelper.makeWithoutCaching(DataVariableMultiplicationFactor.class);
   public final ClassNode BlockInfo = ClassHelper.makeWithoutCaching(BlockInfo.class);
   public final ClassNode Void = ClassHelper.makeWithoutCaching(void.class);
+  public final ClassNode CompileDynamic = ClassHelper.makeWithoutCaching(CompileDynamic.class);
+  public final ClassNode CompileStatic = ClassHelper.makeWithoutCaching(CompileStatic.class);
+  public final ClassNode SpockCompileDynamic = ClassHelper.makeWithoutCaching(SpockCompileDynamic.class);
+  public final ClassNode SpockCompileStatic = ClassHelper.makeWithoutCaching(SpockCompileStatic.class);
+  public final ClassNode SpockTypeChecked = ClassHelper.makeWithoutCaching(SpockTypeChecked.class);
+  public final ClassNode TypeChecked = ClassHelper.makeWithoutCaching(TypeChecked.class);
 
   public final MethodNode Specification_GetSpecificationContext =
     Specification.getDeclaredMethods(Identifiers.GET_SPECIFICATION_CONTEXT).get(0);
@@ -178,6 +185,8 @@ public class AstNodeCache {
     InteractionBuilder.getDeclaredMethods(org.spockframework.mock.runtime.InteractionBuilder.ADD_CODE_ARG).get(0);
   public final MethodNode InteractionBuilder_AddEqualArg =
     InteractionBuilder.getDeclaredMethods(org.spockframework.mock.runtime.InteractionBuilder.ADD_EQUAL_ARG).get(0);
+  public final MethodNode InteractionBuilder_AddSpreadWildcardArg =
+    InteractionBuilder.getDeclaredMethods(org.spockframework.mock.runtime.InteractionBuilder.ADD_SPREAD_WILDCARD_ARG).get(0);
   public final MethodNode InteractionBuilder_AddIterableResponse =
     InteractionBuilder.getDeclaredMethods(org.spockframework.mock.runtime.InteractionBuilder.ADD_ITERABLE_RESPONSE).get(0);
   public final MethodNode InteractionBuilder_AddCodeResponse =
diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java
index 194101c631..8a9987872f 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java
@@ -23,6 +23,7 @@
 import spock.lang.Specification;
 
 import java.util.*;
+import java.util.function.Supplier;
 import java.util.regex.Pattern;
 
 import org.codehaus.groovy.ast.*;
@@ -322,6 +323,16 @@ public static boolean isJointCompiled(ClassNode clazz) {
     return clazz.getModule().getUnit().getConfig().getJointCompilationOptions() != null;
   }
 
+  /**
+   * Creates a fresh expression referencing {@code this}. Generated code must not share
+   * {@link VariableExpression#THIS_EXPRESSION} between use sites, because the static type
+   * checker stores inferred types as per-node metadata; a singleton node used both inside
+   * and outside of closures ends up with conflicting casts in the generated bytecode.
+   */
+  public static VariableExpression createThisExpression() {
+    return new VariableExpression("this");
+  }
+
   public static MethodCallExpression createMethodCall(Expression target, String methodName, Expression arguments) {
     MethodCallExpression result = new MethodCallExpression(target, methodName, arguments);
     // and https://github.com/spockframework/spock/issues/1200
@@ -375,15 +386,15 @@ public static MethodCallExpression createGetAtMethodCall(Expression expression,
     return createMethodCall(expression, GET_AT_METHOD_NAME, new ConstantExpression(key));
   }
 
-  public static Expression createGetAtWithMapSupportMethodCall(Expression expression, int index, String key) {
+  public static Expression createGetAtWithMapSupportMethodCall(Supplier expressionSupplier, int index, String key) {
     return new TernaryExpression(
       new BooleanExpression(
         new BinaryExpression(
-          expression,
+          expressionSupplier.get(),
           Token.newKeyword("instanceof", -1, -1),
           new ClassExpression(ClassHelper.MAP_TYPE))),
-      createGetAtMethodCall(expression, key),
-      createGetAtMethodCall(expression, index)
+      createGetAtMethodCall(expressionSupplier.get(), key),
+      createGetAtMethodCall(expressionSupplier.get(), index)
     );
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/compiler/ConditionRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/ConditionRewriter.java
index 6bfad87439..46b4e8f467 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/ConditionRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/ConditionRewriter.java
@@ -15,6 +15,7 @@
 
 package org.spockframework.compiler;
 
+import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.Parameter;
@@ -27,7 +28,6 @@
 import org.spockframework.runtime.ValueRecorder;
 import org.spockframework.util.AbstractExpressionConverter;
 import org.spockframework.util.Assert;
-import org.spockframework.util.Identifiers;
 import org.spockframework.util.ReflectionUtil;
 import org.spockframework.util.TextUtil;
 
@@ -116,22 +116,40 @@ public void visitMethodCallExpression(MethodCallExpression expr) {
         && (!AstUtil.hasPlausibleSourcePosition(expr.getMethod()) // before GROOVY-4344 fix
             || (expr.getMethod().getColumnNumber() == expr.getObjectExpression().getColumnNumber())); // after GROOVY-4344 fix
 
+    Expression method = expr.getMethod();
     MethodCallExpression conversion =
         new MethodCallExpression(
             expr.isImplicitThis() ?
                 expr.getObjectExpression() :
                 convert(expr.getObjectExpression()),
             objectExprSeenAsMethodNameAtRuntime ?
-                expr.getMethod() :
-                convert(expr.getMethod()),
+                method :
+                convertMethodName(method),
             convert(expr.getArguments()));
 
     conversion.setSafe(expr.isSafe());
     conversion.setSpreadSafe(expr.isSpreadSafe());
+    // must be kept in sync, otherwise a qualified call with a constant method
+    // name would be dispatched as an implicit-this call, e.g. to a local
+    // closure variable of the same name
+    conversion.setImplicitThis(expr.isImplicitThis());
     conversion.setSourcePosition(expr);
     result = record(conversion);
   }
 
+  /**
+   * Wrapping a method name in record() turns the call into a dynamic method name
+   * call, which does not pass static type checking. All constant method names stay as they are;
+   * their value slot is still allocated to keep the numbering intact, it just stays N/A, which the
+   * renderer never displays for method names anyway.
+   */
+  private Expression convertMethodName(Expression method) {
+    if (method instanceof ConstantExpression) {
+      return recordNa(method);
+    }
+    return convert(method);
+  }
+
   // only used for statically imported methods called by their simple name
   @Override
   public void visitStaticMethodCallExpression(StaticMethodCallExpression expr) {
@@ -403,10 +421,20 @@ public void visitMapEntryExpression(MapEntryExpression expr) {
 
   @Override
   public void visitConstructorCallExpression(ConstructorCallExpression expr) {
-    ConstructorCallExpression conversion =
-        new ConstructorCallExpression(
-            expr.getType(),
-            convert(expr.getArguments()));
+    ConstructorCallExpression conversion;
+
+    if ((expr.getArguments() instanceof ArgumentListExpression)
+      && (((ArgumentListExpression) expr.getArguments()).getExpressions().size() == 1)
+      && ((ArgumentListExpression) expr.getArguments()).getExpression(0) instanceof MapExpression) {
+
+      // work-around for https://issues.apache.org/jira/browse/GROOVY-12454
+      ArgumentListExpression converted = ((ArgumentListExpression) convert(expr.getArguments()));
+      Expression mapExpression = converted.getExpressions().remove(0);
+      converted.addExpression(unrecord(mapExpression));
+      conversion = new ConstructorCallExpression(expr.getType(), converted);
+    } else {
+      conversion = new ConstructorCallExpression(expr.getType(), convert(expr.getArguments()));
+    }
 
     conversion.setSourcePosition(expr);
     conversion.setUsingAnonymousInnerClass(expr.isUsingAnonymousInnerClass());
@@ -686,14 +714,39 @@ private Statement rewriteMethodCondition(MethodCallExpression condition, Express
     args.add(primitiveConstExpression(explicit));
     args.add(primitiveConstExpression(lastVariableNum));
 
-    return surroundWithTryCatch(
+    Statement result = surroundWithTryCatch(
+      condition,
+      message,
+      rewriteToSpockRuntimeCall(
+        resources.getAstNodeCache().SpockRuntime_VerifyMethodCondition,
         condition,
         message,
-        rewriteToSpockRuntimeCall(
-            resources.getAstNodeCache().SpockRuntime_VerifyMethodCondition,
-            condition,
-            message,
-            args, optOut), optOut);
+        args, optOut), optOut);
+
+    if (!AstUtil.isInvocationWithImplicitThis(condition)) {
+      ClassNode targetType = AstUtil.getInvocationTarget(condition).getType();
+
+      if (!ClassHelper.OBJECT_TYPE.equals(targetType)
+        && !resources.getAstNodeCache().SpockRuntime.equals(targetType)
+        && (condition.getMethod() instanceof ConstantExpression)) {
+
+        BlockStatement blockStatement = new BlockStatement();
+        blockStatement.addStatement(result);
+        ConstantExpression methodName = new ConstantExpression(((ConstantExpression) condition.getMethod()).getValue());
+        methodName.setSourcePosition(condition.getMethod());
+        blockStatement.addStatement(
+          new IfStatement(
+            new BooleanExpression(new ConstantExpression(false, true)),
+            new ExpressionStatement(
+              new MethodPointerExpression(
+                new CastExpression(targetType, new ConstantExpression(null)),
+                methodName)),
+            new EmptyStatement()));
+        result = blockStatement;
+      }
+    }
+
+    return result;
   }
 
   private Statement rewriteStaticMethodCondition(StaticMethodCallExpression condition, Expression message,
@@ -718,16 +771,35 @@ private Statement rewriteStaticMethodCondition(StaticMethodCallExpression condit
     args.add(primitiveConstExpression(explicit));
     args.add(primitiveConstExpression(lastVariableNum));
 
-    return surroundWithTryCatch(
+    Statement result = surroundWithTryCatch(
+      condition,
+      message,
+      rewriteToSpockRuntimeCall(
+        resources.getAstNodeCache().SpockRuntime_VerifyMethodCondition,
         condition,
         message,
-        rewriteToSpockRuntimeCall(
-            resources.getAstNodeCache().SpockRuntime_VerifyMethodCondition,
-            condition,
-            message,
-            args,
-            optOut),
-        optOut);
+        args,
+        optOut),
+      optOut);
+
+    ClassNode targetType = condition.getOwnerType();
+    if (!ClassHelper.OBJECT_TYPE.equals(targetType)) {
+      BlockStatement blockStatement = new BlockStatement();
+      blockStatement.addStatement(result);
+      ConstantExpression methodName = new ConstantExpression(condition.getMethod());
+      methodName.setSourcePosition(condition);
+      blockStatement.addStatement(
+        new IfStatement(
+          new BooleanExpression(new ConstantExpression(false, true)),
+          new ExpressionStatement(
+            new MethodPointerExpression(
+              new CastExpression(targetType, new ConstantExpression(null)),
+              methodName)),
+          new EmptyStatement()));
+      result = blockStatement;
+    }
+
+    return result;
   }
 
   private Statement rewriteOtherCondition(Expression condition, Expression message, boolean optOut) {
diff --git a/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
index 3b257b1b3d..08bec33d9d 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
@@ -177,7 +177,7 @@ private boolean handleImplicitCallOnMethodParam(MethodCallExpression expr) {
   }
 
   private boolean handleInteraction(InteractionRewriter rewriter, ExpressionStatement stat) {
-    ExpressionStatement interaction = rewriter.rewrite(stat);
+    Statement interaction = rewriter.rewrite(stat);
     if (interaction == null) return false;
 
     // would also want to enforce this for when-blocks, but unfortunately it's not that uncommon
diff --git a/spock-core/src/main/java/org/spockframework/compiler/InteractionRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/InteractionRewriter.java
index 476357a3ff..01849451cc 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/InteractionRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/InteractionRewriter.java
@@ -16,6 +16,7 @@
 
 package org.spockframework.compiler;
 
+import org.codehaus.groovy.ast.stmt.*;
 import org.spockframework.lang.Wildcard;
 import org.spockframework.util.*;
 
@@ -23,12 +24,10 @@
 
 import org.codehaus.groovy.ast.*;
 import org.codehaus.groovy.ast.expr.*;
-import org.codehaus.groovy.ast.stmt.ExpressionStatement;
 import org.codehaus.groovy.syntax.Types;
 
 import static java.util.Arrays.asList;
-import static org.spockframework.compiler.AstUtil.createDirectMethodCall;
-import static org.spockframework.compiler.AstUtil.primitiveConstExpression;
+import static org.spockframework.compiler.AstUtil.*;
 
 /**
  * Creates the AST representation of an InteractionBuilder build sequence.
@@ -51,6 +50,7 @@ public class InteractionRewriter {
   // holds the incrementally constructed expression, which looks roughly as follows:
   // "new InteractionBuilder(..).setCount(..).setTarget(..).setMethod(..).addArg(..).addResult(..).build()"
   private Expression builderExpr;
+  private Statement interactionMethodReferenceStatement;
 
   public InteractionRewriter(IRewriteResources resources, @Nullable ClosureExpression activeWithOrMockClosure) {
     this.resources = resources;
@@ -64,7 +64,7 @@ public InteractionRewriter(IRewriteResources resources, @Nullable ClosureExpress
    * and returns null.
    */
   @Nullable
-  public ExpressionStatement rewrite(ExpressionStatement stat) {
+  public Statement rewrite(ExpressionStatement stat) {
     try {
       if (!isInteraction(stat)) return null;
 
@@ -73,7 +73,14 @@ public ExpressionStatement rewrite(ExpressionStatement stat) {
       setCall();
       addResponses();
       build();
-      return register();
+      Statement result = register();
+      if (interactionMethodReferenceStatement != null) {
+        BlockStatement blockStatement = new BlockStatement();
+        blockStatement.addStatement(result);
+        blockStatement.addStatement(interactionMethodReferenceStatement);
+        result = blockStatement;
+      }
+      return result;
     } catch (InvalidSpecCompileException e) {
       resources.getErrorReporter().error(e);
       return null;
@@ -223,9 +230,23 @@ private void setPropertyName() {
   }
 
   private void setMethodCall() {
-    setTarget();
-    setMethodName();
+    ClassNode targetType = setTarget();
+    Expression methodName = setMethodName();
     addArgs();
+    if (!ClassHelper.OBJECT_TYPE.equals(targetType)
+      && (methodName instanceof ConstantExpression)
+      && !Wildcard.INSTANCE.toString().equals(((ConstantExpression) methodName).getValue())) {
+
+      ConstantExpression copiedMethodName = new ConstantExpression(((ConstantExpression) methodName).getValue());
+      copiedMethodName.setSourcePosition(methodName);
+      interactionMethodReferenceStatement = new IfStatement(
+        new BooleanExpression(new ConstantExpression(false, true)),
+        new ExpressionStatement(
+          new MethodPointerExpression(
+            new CastExpression(targetType, new ConstantExpression(null)),
+            copiedMethodName)),
+        new EmptyStatement());
+    }
   }
 
   private void setConstructorCall() {
@@ -234,19 +255,23 @@ private void setConstructorCall() {
     addArgs();
   }
 
-  private void setTarget()  {
+  private ClassNode setTarget()  {
+    Expression target;
     if (implicitTarget) {
-      call(resources.getAstNodeCache().InteractionBuilder_AddEqualTarget, AstUtil.getImplicitParameterRef(activeWithOrMockClosure));
+      target = AstUtil.getImplicitParameterRef(activeWithOrMockClosure);
     } else {
-      call(resources.getAstNodeCache().InteractionBuilder_AddEqualTarget, AstUtil.getInvocationTarget(call));
+      target = AstUtil.getInvocationTarget(call);
     }
+    call(resources.getAstNodeCache().InteractionBuilder_AddEqualTarget, target);
+    return target.getType();
   }
 
-  private void setMethodName() {
+  private Expression setMethodName() {
     Expression methodNameExpr = ((MethodCallExpression) call).getMethod();
     MethodNode constraint = selectNameConstraint(methodNameExpr,
       resources.getAstNodeCache().InteractionBuilder_AddEqualMethodName, resources.getAstNodeCache().InteractionBuilder_AddRegexMethodName);
     call(constraint, methodNameExpr);
+    return (constraint == resources.getAstNodeCache().InteractionBuilder_AddEqualMethodName) ? methodNameExpr : null;
   }
 
   private MethodNode selectNameConstraint(Expression nameExpr, MethodNode constraint1, MethodNode constraint2) {
@@ -267,7 +292,6 @@ private void addArgs() {
     if (call instanceof PropertyExpression) return;
 
     Expression args = AstUtil.getArguments(call);
-    if (args == ArgumentListExpression.EMPTY_ARGUMENTS) return; // fast lane
 
     if (args instanceof ArgumentListExpression)
       addPositionalArgs((ArgumentListExpression) args);
@@ -350,6 +374,11 @@ private void addArg(Expression arg) {
       return;
     }
 
+    if ((arg instanceof SpreadExpression) && AstUtil.isWildcardRef(((SpreadExpression) arg).getExpression())) {
+      call(resources.getAstNodeCache().InteractionBuilder_AddSpreadWildcardArg);
+      return;
+    }
+
     call(resources.getAstNodeCache().InteractionBuilder_AddEqualArg, arg);
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/compiler/MarkAnnotationsAlreadyPresentInConversionPhase.java b/spock-core/src/main/java/org/spockframework/compiler/MarkAnnotationsAlreadyPresentInConversionPhase.java
new file mode 100644
index 0000000000..4bd39e17aa
--- /dev/null
+++ b/spock-core/src/main/java/org/spockframework/compiler/MarkAnnotationsAlreadyPresentInConversionPhase.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2026 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.spockframework.compiler;
+
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.ASTTransformation;
+import org.codehaus.groovy.transform.GroovyASTTransformation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.codehaus.groovy.control.CompilePhase.CONVERSION;
+
+/**
+ * Mark annotations as already present in conversion phase as for example trait transform adds
+ * code with STC annotations and we don't want to complain about those later on.
+ * We cannot complain here directly, as in conversion phase only the explicitly written annotation
+ * name is known, not the proper type.
+ * We can also not simply mark the annotations themselves, because the annotation collection
+ * could already been done between conversion and semantic analysis phases, which would replace
+ * the marked dynamic compilation annotation with an unmarked static compilation skip annotation.
+ */
+@GroovyASTTransformation(phase = CONVERSION)
+public class MarkAnnotationsAlreadyPresentInConversionPhase implements ASTTransformation {
+  @Override
+  public void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
+    List classes = sourceUnit.getAST().getClasses();
+
+    for (ClassNode classNode : classes) {
+      new ClassCodeVisitorSupport() {
+        @Override
+        protected SourceUnit getSourceUnit() {
+          return sourceUnit;
+        }
+
+        @Override
+        public void visitAnnotations(AnnotatedNode node) {
+          List annotations = node.getAnnotations();
+          if (!annotations.isEmpty()) {
+            node.putNodeMetaData(MarkAnnotationsAlreadyPresentInConversionPhase.class, new ArrayList<>(annotations));
+          }
+
+          super.visitAnnotations(node);
+        }
+      }.visitClass(classNode);
+    }
+  }
+}
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index 1911fe7c72..f2dc52561c 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -470,7 +470,7 @@ private MethodCallExpression createBlockListenerCall(Block block, MethodNode blo
       new ClassExpression(nodeCache.SpockRuntime),
       blockListenerMethod,
       new ArgumentListExpression(
-        VariableExpression.THIS_EXPRESSION,
+        AstUtil.createThisExpression(),
         new ConstantExpression(block.getBlockMetaDataIndex(), true)
       ));
   }
@@ -693,7 +693,7 @@ public VariableExpression captureOldValue(Expression oldValue) {
   }
 
   public MethodCallExpression getSpecificationContext() {
-    return createDirectMethodCall(VariableExpression.THIS_EXPRESSION,
+    return createDirectMethodCall(AstUtil.createThisExpression(),
         nodeCache.Specification_GetSpecificationContext, ArgumentListExpression.EMPTY_ARGUMENTS);
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecialMethodCall.java b/spock-core/src/main/java/org/spockframework/compiler/SpecialMethodCall.java
index dcfaefb304..2bb37e81bd 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecialMethodCall.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecialMethodCall.java
@@ -174,7 +174,7 @@ public ClosureExpression getClosureExpr() {
   @Override
   public void expand() {
     List args = new ArrayList<>();
-    args.add(VariableExpression.THIS_EXPRESSION);
+    args.add(AstUtil.createThisExpression());
     args.add(inferredName);
     args.add(inferredType);
     args.addAll(AstUtil.getArgumentList(methodCallExpr));
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpockTransform.java b/spock-core/src/main/java/org/spockframework/compiler/SpockTransform.java
index b2eaff4282..0d59b56aec 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpockTransform.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpockTransform.java
@@ -16,16 +16,19 @@
 
 package org.spockframework.compiler;
 
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.classgen.VariableScopeVisitor;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.ASTTransformation;
+import org.codehaus.groovy.transform.ASTTransformationCollectorCodeVisitor;
+import org.codehaus.groovy.transform.GroovyASTTransformation;
 import org.spockframework.compiler.model.Spec;
 import org.spockframework.util.VersionChecker;
 
 import java.util.List;
 
-import org.codehaus.groovy.ast.*;
-import org.codehaus.groovy.control.*;
-import org.codehaus.groovy.transform.*;
-
 /**
  * AST transformation for rewriting Spock specifications. Runs after phase
  * SEMANTIC_ANALYSIS, which means that the AST is semantically accurate
@@ -59,7 +62,11 @@ void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
         List classes = sourceUnit.getAST().getClasses();
 
         for (ClassNode clazz : classes)
-          if (isSpec(clazz)) processSpec(sourceUnit, clazz, errorReporter, sourceLookup);
+          if (isSpec(clazz)) {
+            processSpec(sourceUnit, clazz, errorReporter, sourceLookup);
+          } else {
+            new StcAnnotationsUsageVisitor(nodeCache, sourceUnit).visitClass(clazz);
+          }
       }
     }
 
@@ -69,6 +76,11 @@ boolean isSpec(ClassNode clazz) {
 
     void processSpec(SourceUnit sourceUnit, ClassNode clazz, ErrorReporter errorReporter, SourceLookup sourceLookup) {
       try {
+        new StcAnnotationsVisitor(nodeCache, sourceUnit).visitClass(clazz);
+        // the StcAnnotationsVisitor could have added CompileDynamic, but the annotation
+        // collecting could already be finished, so do it again explicitly here
+        new ASTTransformationCollectorCodeVisitor(sourceUnit, clazz.getCompileUnit().getClassLoader());
+
         Spec spec = new SpecParser(nodeCache, errorReporter).build(clazz);
         spec.accept(new SpecRewriter(nodeCache, sourceLookup, errorReporter));
         spec.accept(new SpecAnnotator(nodeCache));
@@ -78,8 +90,8 @@ void processSpec(SourceUnit sourceUnit, ClassNode clazz, ErrorReporter errorRepo
         }
       } catch (Exception e) {
         errorReporter.error(
-            "Unexpected error during compilation of spec '%s'. Maybe you have used invalid Spock syntax? Anyway, please file a bug report at https://issues.spockframework.org.",
-            e, clazz.getName());
+          "Unexpected error during compilation of spec '%s'. Maybe you have used invalid Spock syntax? Anyway, please file a bug report at https://issues.spockframework.org.",
+          e, clazz.getName());
       }
     }
   }
diff --git a/spock-core/src/main/java/org/spockframework/compiler/StcAnnotationsUsageVisitor.java b/spock-core/src/main/java/org/spockframework/compiler/StcAnnotationsUsageVisitor.java
new file mode 100644
index 0000000000..9ea66e777b
--- /dev/null
+++ b/spock-core/src/main/java/org/spockframework/compiler/StcAnnotationsUsageVisitor.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2026 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.spockframework.compiler;
+
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+import java.util.List;
+
+import static org.spockframework.util.ObjectUtil.uncheckedCast;
+
+public class StcAnnotationsUsageVisitor extends ClassCodeVisitorSupport {
+  private final AstNodeCache nodeCache;
+  private final SourceUnit sourceUnit;
+
+  public StcAnnotationsUsageVisitor(AstNodeCache nodeCache, SourceUnit sourceUnit) {
+    this.nodeCache = nodeCache;
+    this.sourceUnit = sourceUnit;
+  }
+
+  @Override
+  protected SourceUnit getSourceUnit() {
+    return sourceUnit;
+  }
+
+  @Override
+  public void visitAnnotations(AnnotatedNode node) {
+    List originalAnnotations = uncheckedCast(node.getNodeMetaData(MarkAnnotationsAlreadyPresentInConversionPhase.class));
+    if (originalAnnotations != null) {
+      for (AnnotationNode an : originalAnnotations) {
+        if ((nodeCache.SpockCompileDynamic.equals(an.getClassNode())
+          || nodeCache.SpockCompileStatic.equals(an.getClassNode())
+          || nodeCache.SpockTypeChecked.equals(an.getClassNode()))) {
+
+          sourceUnit.getErrorCollector().addErrorAndContinue(new SyntaxErrorMessage(
+            new SyntaxException("SpockCompileDynamic, SpockCompileStatic, and SpockTypeChecked annotations are not allowed outside Spock specifications, use CompileDynamic, CompileStatic, or TypeChecked instead", an),
+            sourceUnit));
+        }
+      }
+    }
+
+    super.visitAnnotations(node);
+  }
+}
diff --git a/spock-core/src/main/java/org/spockframework/compiler/StcAnnotationsVisitor.java b/spock-core/src/main/java/org/spockframework/compiler/StcAnnotationsVisitor.java
new file mode 100644
index 0000000000..56a9675101
--- /dev/null
+++ b/spock-core/src/main/java/org/spockframework/compiler/StcAnnotationsVisitor.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2026 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.spockframework.compiler;
+
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+import java.util.List;
+import java.util.Objects;
+
+import static java.util.stream.Collectors.toList;
+import static org.spockframework.util.ObjectUtil.uncheckedCast;
+
+public class StcAnnotationsVisitor extends ClassCodeVisitorSupport {
+  private final AstNodeCache nodeCache;
+  private final SourceUnit sourceUnit;
+
+  public StcAnnotationsVisitor(AstNodeCache nodeCache, SourceUnit sourceUnit) {
+    this.nodeCache = nodeCache;
+    this.sourceUnit = sourceUnit;
+  }
+
+  @Override
+  protected SourceUnit getSourceUnit() {
+    return sourceUnit;
+  }
+
+  @Override
+  public void visitAnnotations(AnnotatedNode node) {
+    List originalAnnotations = uncheckedCast(node.getNodeMetaData(MarkAnnotationsAlreadyPresentInConversionPhase.class));
+    if (originalAnnotations != null) {
+      for (AnnotationNode an : originalAnnotations) {
+        if ((nodeCache.CompileDynamic.equals(an.getClassNode())
+          || nodeCache.CompileStatic.equals(an.getClassNode())
+          || nodeCache.TypeChecked.equals(an.getClassNode()))) {
+
+          sourceUnit.getErrorCollector().addErrorAndContinue(new SyntaxErrorMessage(
+            new SyntaxException("CompileDynamic, CompileStatic, and TypeChecked annotations are not allowed within Spock specifications as the IDE would majorly misreport type problems, use SpockCompileDynamic, SpockCompileStatic, or SpockTypeChecked instead", an),
+            sourceUnit));
+        }
+      }
+    }
+
+    super.visitAnnotations(node);
+
+    List annotations = node.getAnnotations();
+    annotations
+      .stream()
+      .map(annotation ->
+      {
+        ClassNode builtInAnnotation = null;
+        if (nodeCache.SpockCompileDynamic.equals(annotation.getClassNode())) {
+          builtInAnnotation = nodeCache.CompileDynamic;
+        } else if (nodeCache.SpockCompileStatic.equals(annotation.getClassNode())) {
+          builtInAnnotation = nodeCache.CompileStatic;
+        } else if (nodeCache.SpockTypeChecked.equals(annotation.getClassNode())) {
+          builtInAnnotation = nodeCache.TypeChecked;
+        }
+
+        if (builtInAnnotation == null) {
+          return null;
+        }
+
+        AnnotationNode newAnnotation = new AnnotationNode(builtInAnnotation);
+        newAnnotation.setSourcePosition(annotation);
+        annotation.getMembers().forEach(newAnnotation::setMember);
+
+        return newAnnotation;
+      })
+      .filter(Objects::nonNull)
+      .collect(toList())
+      .forEach(node::addAnnotation);
+  }
+}
diff --git a/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java
index 9ec7991d54..ea2a2bb8bb 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java
@@ -25,6 +25,7 @@
 
 import java.util.*;
 import java.util.function.Function;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -454,6 +455,12 @@ private List getExpressionChain(ListIterator stats) {
     return result;
   }
 
+  private void transplantStcAnnotations(MethodNode method) {
+    whereBlock.getParent().getAst().getAnnotations(resources.getAstNodeCache().CompileDynamic).forEach(method::addAnnotation);
+    whereBlock.getParent().getAst().getAnnotations(resources.getAstNodeCache().TypeChecked).forEach(method::addAnnotation);
+    whereBlock.getParent().getAst().getAnnotations(resources.getAstNodeCache().CompileStatic).forEach(method::addAnnotation);
+  }
+
   private void createDataProviderMethod(Expression dataProviderExpr, int nextDataVariableIndex, boolean addDataTableParameters) {
     instanceFieldAccessChecker.check(dataProviderExpr);
 
@@ -480,6 +487,7 @@ private void createDataProviderMethod(Expression dataProviderExpr, int nextDataV
         new BlockStatement(dataProviderStats, null));
 
     method.addAnnotation(createDataProviderAnnotation(dataProviderExpr, nextDataVariableIndex, addDataTableParameters));
+    transplantStcAnnotations(method);
     whereBlock.getParent().getParent().getAst().addMethod(method);
   }
 
@@ -562,11 +570,11 @@ private void rewriteMultiParameterization(BinaryExpression binExpr, Statement en
     int nextDataVariableIndex = dataProcessorVars.size();
     Parameter dataProcessorParameter = createDataProcessorParameter();
     ListExpression list = (ListExpression) binExpr.getLeftExpression();
-    rewriteMultiParameterization(list, new VariableExpression(dataProcessorParameter), enclosingStat);
+    rewriteMultiParameterization(list, () -> new VariableExpression(dataProcessorParameter), enclosingStat);
     createDataProviderMethod(binExpr.getRightExpression(), nextDataVariableIndex, false);
   }
 
-  private void rewriteMultiParameterization(ListExpression list, Expression rightBase, Statement enclosingStat)
+  private void rewriteMultiParameterization(ListExpression list, Supplier rightBaseSupplier, Statement enclosingStat)
       throws InvalidSpecCompileException {
     List listElems = list.getExpressions();
     for (int i = 0; i < listElems.size(); i++) {
@@ -575,11 +583,11 @@ private void rewriteMultiParameterization(ListExpression list, Expression rightB
 
       if (listElem instanceof VariableExpression) {
         VariableExpression variable = createDataProcessorVariable(listElem, enclosingStat);
-        createDataProcessorStatement(variable, createGetAtWithMapSupportMethodCall(rightBase, i, variable.getName()), enclosingStat);
+        createDataProcessorStatement(variable, createGetAtWithMapSupportMethodCall(rightBaseSupplier, i, variable.getName()), enclosingStat);
       } else if (listElem instanceof ListExpression) {
-        VariableExpression variable = new VariableExpression("$spock_l" + localVariableCount++);
-        createDataProcessorStatement(variable, createGetAtMethodCall(rightBase, i), enclosingStat);
-        rewriteMultiParameterization(((ListExpression) listElem), variable, enclosingStat);
+        String variableName = "$spock_l" + localVariableCount++;
+        createDataProcessorStatement(new VariableExpression(variableName), createGetAtMethodCall(rightBaseSupplier.get(), i), enclosingStat);
+        rewriteMultiParameterization(((ListExpression) listElem), () -> new VariableExpression(variableName), enclosingStat);
       } else {
         throw notAParameterization(enclosingStat);
       }
@@ -804,18 +812,36 @@ private void turnIntoSimpleParameterization(List column) throws Inva
   private void generatePreviousColumnExtractorStatements(Set referencedPreviousVariables, int row,
                                                          List statements) {
     for (String referencedPreviousVariable : referencedPreviousVariables) {
+      ClassNode declaredType = getDeclaredDataVariableType(referencedPreviousVariable);
+      Expression rowValue = createDirectMethodCall(
+        new VariableExpression(getDataTableParameterName(referencedPreviousVariable)),
+        resources.getAstNodeCache().List_Get,
+        new ConstantExpression(row));
+      if (declaredType != null) {
+        // use the declared feature parameter type, so that cell expressions
+        // referencing previous columns pass static type checking
+        CastExpression coercedRowValue = new CastExpression(declaredType, rowValue);
+        coercedRowValue.setCoerce(true);
+        rowValue = coercedRowValue;
+      }
       statements.add(new ExpressionStatement(
+        // Type x = $spock_p_x.get(row) as Type
+        // or, without a declared parameter type
         // def x = $spock_p_x.get(row)
         new DeclarationExpression(
-          new VariableExpression(referencedPreviousVariable),
+          new VariableExpression(referencedPreviousVariable, declaredType == null ? ClassHelper.DYNAMIC_TYPE : declaredType),
           Token.newSymbol(Types.ASSIGN, -1, -1),
-          createDirectMethodCall(
-            new VariableExpression(getDataTableParameterName(referencedPreviousVariable)),
-            resources.getAstNodeCache().List_Get,
-            new ConstantExpression(row)))));
+          rowValue)));
     }
   }
 
+  private ClassNode getDeclaredDataVariableType(String name) {
+    for (Parameter parameter : whereBlock.getParent().getAst().getParameters()) {
+      if (parameter.getName().equals(name) && !parameter.isDynamicTyped()) return parameter.getType();
+    }
+    return null;
+  }
+
   private List getReferencedPreviousVariables(List previousVariables, Expression providerExpression) {
     return previousVariables
       .stream()
@@ -951,6 +977,7 @@ private void createDataProcessorMethod() {
       ClassNode.EMPTY_ARRAY,
       blockStat);
     dataProcessorMethod.addAnnotation(createDataProcessorAnnotation());
+    transplantStcAnnotations(dataProcessorMethod);
 
     whereBlock.getParent().getParent().getAst().addMethod(dataProcessorMethod);
   }
@@ -1025,6 +1052,7 @@ private void createWhereVariablesMethod() {
       Parameter.EMPTY_ARRAY,
       ClassNode.EMPTY_ARRAY,
       new BlockStatement(Arrays.asList(valuesDecl, tryCatch), null));
+    transplantStcAnnotations(method);
 
     whereBlock.getParent().getParent().getAst().addMethod(method);
   }
@@ -1073,6 +1101,7 @@ private void createDataVariableMultiplicationsMethod() {
       Parameter.EMPTY_ARRAY,
       ClassNode.EMPTY_ARRAY,
       blockStat);
+    transplantStcAnnotations(dataVariableMultiplicationsMethod);
 
     whereBlock.getParent().getParent().getAst().addMethod(dataVariableMultiplicationsMethod);
   }
@@ -1109,6 +1138,7 @@ private void createFilterMethod() {
         createWhereVariableParameters()),
       ClassNode.EMPTY_ARRAY,
       blockStat);
+    transplantStcAnnotations(filterMethod);
 
     filterBlock.getParent().getParent().getAst().addMethod(filterMethod);
   }
diff --git a/spock-core/src/main/java/org/spockframework/compiler/condition/BaseVerifyMethodTransform.java b/spock-core/src/main/java/org/spockframework/compiler/condition/BaseVerifyMethodTransform.java
index 152596cab5..c3f24fabcd 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/condition/BaseVerifyMethodTransform.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/condition/BaseVerifyMethodTransform.java
@@ -15,6 +15,7 @@
 
 import org.codehaus.groovy.ast.ASTNode;
 import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.classgen.VariableScopeVisitor;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.transform.ASTTransformation;
 import org.spockframework.compiler.*;
@@ -53,12 +54,12 @@ public void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
           errorReporter.error(node, "Method '%s' cannot be annotated with both @Verify and @VerifyAll.", methodNode.getName());
         }
 
-        processVerificationHelperMethod(createVerifyMethod(methodNode), errorReporter, sourceLookup);
+        processVerificationHelperMethod(createVerifyMethod(methodNode), errorReporter, sourceLookup, sourceUnit);
       }
     }
   }
 
-  private void processVerificationHelperMethod(Method method, ErrorReporter errorReporter, SourceLookup sourceLookup) {
+  private void processVerificationHelperMethod(Method method, ErrorReporter errorReporter, SourceLookup sourceLookup, SourceUnit sourceUnit) {
     MethodNode methodAst = method.getAst();
     if (!VerifyMethod.verifyReturnType(methodAst, errorReporter)) {
       return;
@@ -73,6 +74,10 @@ private void processVerificationHelperMethod(Method method, ErrorReporter errorR
 
     try {
       rewriter.rewrite();
+      // if there were no errors so far, let the variable scope visitor fix up variable scopes
+      if (!sourceUnit.getErrorCollector().hasErrors()) {
+        new VariableScopeVisitor(sourceUnit).visitMethod(methodAst);
+      }
     } catch (Exception e) {
       errorReporter.error(
           "Unexpected error during compilation of verification helper method '%s'. Maybe you have used invalid Spock syntax? Anyway, please file a bug report at https://issues.spockframework.org.",
diff --git a/spock-core/src/main/java/org/spockframework/mock/IMockFactory.java b/spock-core/src/main/java/org/spockframework/mock/IMockFactory.java
index 2275877e6c..d23ab6b742 100644
--- a/spock-core/src/main/java/org/spockframework/mock/IMockFactory.java
+++ b/spock-core/src/main/java/org/spockframework/mock/IMockFactory.java
@@ -18,6 +18,6 @@
 
 public interface IMockFactory {
   boolean canCreate(IMockConfiguration configuration);
-  Object create(IMockConfiguration configuration, Specification specification);
-  Object createDetached(IMockConfiguration configuration, ClassLoader classLoader);
+   T create(IMockConfiguration configuration, Specification specification);
+   T createDetached(IMockConfiguration configuration, ClassLoader classLoader);
 }
diff --git a/spock-core/src/main/java/org/spockframework/mock/MockUtil.java b/spock-core/src/main/java/org/spockframework/mock/MockUtil.java
index 8ff03087da..1d80b99a30 100644
--- a/spock-core/src/main/java/org/spockframework/mock/MockUtil.java
+++ b/spock-core/src/main/java/org/spockframework/mock/MockUtil.java
@@ -115,10 +115,29 @@ public void detachMock(Object object) {
    * @param classloader the classloader to use
    * @return the mock
    */
-  public Object createDetachedMock(@Nullable String name, Type type, MockNature nature,
+  public  T createDetachedMock(@Nullable String name, Type type, MockNature nature,
       MockImplementation implementation, Map options,  ClassLoader classloader) {
 
-    Object mock = CompositeMockFactory.INSTANCE.createDetached(
+    T mock = CompositeMockFactory.INSTANCE.createDetached(
+        new MockConfiguration(name, type, nature, implementation, options), classloader);
+    return mock;
+  }
+
+  /**
+   * Creates a detached mock.
+   *
+   * @param name the name
+   * @param type the type of the mock
+   * @param nature the nature
+   * @param implementation the implementation
+   * @param options the options
+   * @param classloader the classloader to use
+   * @return the mock
+   */
+  public  T createDetachedMock(@Nullable String name, Class type, MockNature nature,
+      MockImplementation implementation, Map options,  ClassLoader classloader) {
+
+    T mock = CompositeMockFactory.INSTANCE.createDetached(
         new MockConfiguration(name, type, nature, implementation, options), classloader);
     return mock;
   }
@@ -134,10 +153,10 @@ public Object createDetachedMock(@Nullable String name, Type type, MockNature na
    * @param classloader the classloader to use
    * @return the mock
    */
-  public Object createDetachedMock(@Nullable String name, Object obj, MockNature nature,
+  public  T createDetachedMock(@Nullable String name, T obj, MockNature nature,
       MockImplementation implementation, Map options,  ClassLoader classloader) {
 
-    Object mock = CompositeMockFactory.INSTANCE.createDetached(
+    T mock = CompositeMockFactory.INSTANCE.createDetached(
         new MockConfiguration(name, obj.getClass(), obj, nature, implementation, options), classloader);
     return mock;
   }
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/ByteBuddyMockFactory.java b/spock-core/src/main/java/org/spockframework/mock/runtime/ByteBuddyMockFactory.java
index 583a5fef62..ee6957e201 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/ByteBuddyMockFactory.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/ByteBuddyMockFactory.java
@@ -51,6 +51,7 @@
 
 import static java.util.Objects.requireNonNull;
 import static net.bytebuddy.matcher.ElementMatchers.none;
+import static org.spockframework.util.ObjectUtil.uncheckedCast;
 
 class ByteBuddyMockFactory {
   /**
@@ -111,7 +112,7 @@ static boolean isLocalMock(Class targetClass, Collection> additional
     return cl == targetClass.getClassLoader();
   }
 
-  Object createMock(IMockMaker.IMockCreationSettings settings) {
+   T createMock(IMockMaker.IMockCreationSettings settings) {
     final Class type = settings.getMockType();
     TypeCache.SimpleKey key = new TypeCache.SimpleKey(type, settings.getAdditionalInterface());
     ClassLoader classLoader = settings.getClassLoader();
@@ -155,7 +156,7 @@ Object createMock(IMockMaker.IMockCreationSettings settings) {
           .getLoaded();
       }, getCacheLockForKey(key));
 
-    Object proxy = MockInstantiator.instantiate(type, enhancedType, settings.getConstructorArgs(), settings.isUseObjenesis());
+    T proxy = uncheckedCast(MockInstantiator.instantiate(type, enhancedType, settings.getConstructorArgs(), settings.isUseObjenesis()));
     ((ByteBuddyInterceptorAdapter.InterceptorAccess) proxy).$spock_set(settings.getMockInterceptor());
     return proxy;
   }
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/ByteBuddyMockMaker.java b/spock-core/src/main/java/org/spockframework/mock/runtime/ByteBuddyMockMaker.java
index 05ddd399a5..a44a9e0af4 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/ByteBuddyMockMaker.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/ByteBuddyMockMaker.java
@@ -59,7 +59,7 @@ public int getPriority() {
   }
 
   @Override
-  public Object makeMock(IMockCreationSettings settings) throws CannotCreateMockException {
+  public  T makeMock(IMockCreationSettings settings) throws CannotCreateMockException {
     return requireNonNull(mockFactory).createMock(settings);
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/CglibMockFactory.java b/spock-core/src/main/java/org/spockframework/mock/runtime/CglibMockFactory.java
index 7d18cacf64..14499a04d8 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/CglibMockFactory.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/CglibMockFactory.java
@@ -14,11 +14,13 @@
 import java.util.Arrays;
 import java.util.List;
 
+import static org.spockframework.util.ObjectUtil.uncheckedCast;
+
 class CglibMockFactory {
 
   private static final Class[] CLASSES = new Class[0];
 
-  static Object createMock(IMockMaker.IMockCreationSettings settings) {
+  static  T createMock(IMockMaker.IMockCreationSettings settings) {
     Class type = settings.getMockType();
     Enhancer enhancer = new ConstructorFriendlyEnhancer();
     enhancer.setClassLoader(settings.getClassLoader());
@@ -36,7 +38,7 @@ static Object createMock(IMockMaker.IMockCreationSettings settings) {
     enhancer.setCallbackTypes(new Class[] {cglibInterceptor.getClass(), NoOp.class});
 
     Class enhancedType = enhancer.createClass();
-    Object proxy = MockInstantiator.instantiate(type, enhancedType, settings.getConstructorArgs(), settings.isUseObjenesis());
+    T proxy = uncheckedCast(MockInstantiator.instantiate(type, enhancedType, settings.getConstructorArgs(), settings.isUseObjenesis()));
     ((Factory) proxy).setCallbacks(new Callback[] {cglibInterceptor, NoOp.INSTANCE});
     return proxy;
   }
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/CglibMockMaker.java b/spock-core/src/main/java/org/spockframework/mock/runtime/CglibMockMaker.java
index 1cbb8879d3..8d4dcdfc2e 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/CglibMockMaker.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/CglibMockMaker.java
@@ -53,7 +53,7 @@ public int getPriority() {
   }
 
   @Override
-  public Object makeMock(IMockCreationSettings settings) throws CannotCreateMockException {
+  public  T makeMock(IMockCreationSettings settings) throws CannotCreateMockException {
     return CglibMockFactory.createMock(settings);
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/CompositeMockFactory.java b/spock-core/src/main/java/org/spockframework/mock/runtime/CompositeMockFactory.java
index 8b4aa4ecb2..b305c7d010 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/CompositeMockFactory.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/CompositeMockFactory.java
@@ -38,7 +38,7 @@ public boolean canCreate(IMockConfiguration configuration) {
   }
 
   @Override
-  public Object create(IMockConfiguration configuration, Specification specification) {
+  public  T create(IMockConfiguration configuration, Specification specification) {
     for (IMockFactory factory : mockFactories) {
       if (factory.canCreate(configuration)) {
         return factory.create(configuration, specification);
@@ -49,7 +49,7 @@ public Object create(IMockConfiguration configuration, Specification specificati
   }
 
 	@Override
-  public Object createDetached(IMockConfiguration configuration,
+  public  T createDetached(IMockConfiguration configuration,
                                ClassLoader classLoader) {
 	  for (IMockFactory factory : mockFactories) {
       if (factory.canCreate(configuration)) {
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyMockFactory.java b/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyMockFactory.java
index 4e9e4a33ac..6fc2181abd 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyMockFactory.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyMockFactory.java
@@ -32,6 +32,8 @@
 
 import groovy.lang.*;
 
+import static org.spockframework.util.ObjectUtil.uncheckedCast;
+
 public class GroovyMockFactory implements IMockFactory {
 
   private static final ExclusiveResource META_CLASS_REGISTRY_RW = new ExclusiveResource(Resources.META_CLASS_REGISTRY,
@@ -47,7 +49,7 @@ public boolean canCreate(IMockConfiguration configuration) {
   }
 
   @Override
-  public Object create(IMockConfiguration configuration, Specification specification) throws CannotCreateMockException {
+  public  T create(IMockConfiguration configuration, Specification specification) throws CannotCreateMockException {
     final Class type = configuration.getType();
     final MetaClass oldMetaClass = GroovyRuntimeUtil.getMetaClass(configuration.getType());
     if (oldMetaClass instanceof GroovyMockMetaClass) {
@@ -78,7 +80,7 @@ public Object create(IMockConfiguration configuration, Specification specificati
       if (isAbstractClass(type)) {
         return createProxyObjectForAbstractGlobalMock(configuration, specification, newMetaClass);
       }
-      return MockInstantiator.instantiate(type, type, configuration.getConstructorArgs(), configuration.isUseObjenesis());
+      return uncheckedCast(MockInstantiator.instantiate(type, type, configuration.getConstructorArgs(), configuration.isUseObjenesis()));
     }
 
     if (isFinalClass(type)) {
@@ -86,14 +88,14 @@ public Object create(IMockConfiguration configuration, Specification specificati
         throw new CannotCreateMockException(type,
           ". Cannot add additionalInterfaces to final classes.");
       }
-      final Object instance = MockInstantiator.instantiate(type,
-          type, configuration.getConstructorArgs(), configuration.isUseObjenesis());
+      final T instance = uncheckedCast(MockInstantiator.instantiate(type,
+          type, configuration.getConstructorArgs(), configuration.isUseObjenesis()));
       GroovyRuntimeUtil.setMetaClass(instance, newMetaClass);
 
       return instance;
     }
     GroovyMockInterceptor mockInterceptor = new GroovyMockInterceptor(configuration, specification, newMetaClass);
-    Object proxy = createMockObject(configuration, specification, mockInterceptor);
+    T proxy = createMockObject(configuration, specification, mockInterceptor);
 
     if (hasAdditionalInterfaces) {
       //Issue #1405: We need to update the mockMetaClass to reflect the methods of the additional interfaces
@@ -129,16 +131,16 @@ private boolean isIsolatedOrHasMetaClassRegistryReadWriteLock(Specification spec
     return feature.getExclusiveResources().contains(META_CLASS_REGISTRY_RW);
   }
 
-  private Object createProxyObjectForAbstractGlobalMock(IMockConfiguration configuration, Specification specification, GroovyMockMetaClass mockMetaClass) {
+  private  T createProxyObjectForAbstractGlobalMock(IMockConfiguration configuration, Specification specification, GroovyMockMetaClass mockMetaClass) {
     IProxyBasedMockInterceptor mockInterceptor = new GroovyMockInterceptor(configuration, specification, mockMetaClass);
     //Issue #464: We need to create a subclass mock here to be able to instantiate the abstract class.
-    Object proxy = createMockObject(configuration, specification, mockInterceptor);
+    T proxy = createMockObject(configuration, specification, mockInterceptor);
     //We need also to replace the metaClass of the Proxy Object to ensure correct usage of the mock object.
     GroovyRuntimeUtil.setMetaClass(proxy, mockMetaClass);
     return proxy;
   }
 
-  private Object createMockObject(IMockConfiguration configuration, Specification specification, IProxyBasedMockInterceptor mockInterceptor) {
+  private  T createMockObject(IMockConfiguration configuration, Specification specification, IProxyBasedMockInterceptor mockInterceptor) {
     IMockMaker.IMockCreationSettings mockCreationSettings = MockCreationSettings.settingsFromMockConfiguration(configuration,
       mockInterceptor,
       specification.getClass().getClassLoader());
@@ -155,7 +157,7 @@ private boolean isAbstractClass(Class type) {
   }
 
   @Override
-  public Object createDetached(IMockConfiguration configuration, ClassLoader classLoader) {
+  public  T createDetached(IMockConfiguration configuration, ClassLoader classLoader) {
     throw new CannotCreateMockException(configuration.getType(),
         ". Detached mocking is only possible for JavaMocks but not GroovyMocks at the moment.");
   }
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/IMockMaker.java b/spock-core/src/main/java/org/spockframework/mock/runtime/IMockMaker.java
index 3df0653e0a..c46007f911 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/IMockMaker.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/IMockMaker.java
@@ -88,7 +88,7 @@ public interface IMockMaker {
    * @return the new mock instance
    * @throws CannotCreateMockException if this {@code IMockMaker} can't create such a mock.
    */
-  Object makeMock(IMockCreationSettings settings) throws CannotCreateMockException;
+   T makeMock(IMockCreationSettings settings) throws CannotCreateMockException;
 
   /**
    * Creates a new static mock for the passed {@link IMockCreationSettings}.
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/InteractionBuilder.java b/spock-core/src/main/java/org/spockframework/mock/runtime/InteractionBuilder.java
index aceec7c416..21b2fdb16d 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/InteractionBuilder.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/InteractionBuilder.java
@@ -148,6 +148,12 @@ public InteractionBuilder addEqualArg(Object arg) {
     return this;
   }
 
+  public static final String ADD_SPREAD_WILDCARD_ARG = "addSpreadWildcardArg";
+  public InteractionBuilder addSpreadWildcardArg() {
+    argConstraints.add(SpreadWildcardArgumentConstraint.INSTANCE);
+    return this;
+  }
+
   public static final String TYPE_LAST_ARG = "typeLastArg";
   public InteractionBuilder typeLastArg(Class type) {
     IArgumentConstraint last = argConstraints.get(argConstraints.size() - 1);
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/JavaMockFactory.java b/spock-core/src/main/java/org/spockframework/mock/runtime/JavaMockFactory.java
index 5d28e68db3..88ca1673b6 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/JavaMockFactory.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/JavaMockFactory.java
@@ -38,22 +38,22 @@ public boolean canCreate(IMockConfiguration configuration) {
   }
 
   @Override
-  public Object create(IMockConfiguration configuration, Specification specification) {
+  public  T create(IMockConfiguration configuration, Specification specification) {
     return createInternal(configuration, specification, specification.getClass().getClassLoader());
   }
 
-	@Override
-  public Object createDetached(IMockConfiguration configuration, ClassLoader classLoader) {
-		 return createInternal(configuration, null, classLoader);
-	}
+  @Override
+  public  T createDetached(IMockConfiguration configuration, ClassLoader classLoader) {
+    return createInternal(configuration, null, classLoader);
+  }
 
-  private Object createInternal(IMockConfiguration configuration, Specification specification, ClassLoader classLoader) {
+  private  T createInternal(IMockConfiguration configuration, Specification specification, ClassLoader classLoader) {
     Class type = configuration.getType();
     checkNotGlobal(configuration);
 
     MetaClass mockMetaClass = GroovyRuntimeUtil.getMetaClass(type);
     JavaMockInterceptor interceptor = new JavaMockInterceptor(configuration, specification, mockMetaClass);
-    Object proxy = getMockMakerRegistry().makeMock(MockCreationSettings.settingsFromMockConfiguration(configuration, interceptor, classLoader));
+    T proxy = getMockMakerRegistry().makeMock(MockCreationSettings.settingsFromMockConfiguration(configuration, interceptor, classLoader));
     List> additionalInterfaces = configuration.getAdditionalInterfaces();
     if (!additionalInterfaces.isEmpty() && GroovyObject.class.isAssignableFrom(type)) {
       //Issue #1405: We need to update the mockMetaClass to reflect the methods of the additional interfaces
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/JavaProxyMockMaker.java b/spock-core/src/main/java/org/spockframework/mock/runtime/JavaProxyMockMaker.java
index cb6d0e638c..393af12b2a 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/JavaProxyMockMaker.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/JavaProxyMockMaker.java
@@ -29,6 +29,8 @@
 import java.util.List;
 import java.util.Set;
 
+import static org.spockframework.util.ObjectUtil.uncheckedCast;
+
 @ThreadSafe
 public class JavaProxyMockMaker implements IMockMaker {
   public static final MockMakerId ID = new MockMakerId("java-proxy");
@@ -54,20 +56,20 @@ public int getPriority() {
   }
 
   @Override
-  public Object makeMock(IMockCreationSettings settings) throws CannotCreateMockException {
+  public  T makeMock(IMockCreationSettings settings) throws CannotCreateMockException {
     return createMock(settings);
   }
 
-  private Object createMock(IMockMaker.IMockCreationSettings settings) {
+  private  T createMock(IMockMaker.IMockCreationSettings settings) {
     List> interfaces = new ArrayList<>();
     interfaces.add(settings.getMockType());
     interfaces.addAll(settings.getAdditionalInterface());
     interfaces.add(ISpockMockObject.class);
-    return Proxy.newProxyInstance(
+    return uncheckedCast(Proxy.newProxyInstance(
       settings.getClassLoader(),
       interfaces.toArray(CLASSES),
       new JavaProxyMockInterceptorAdapter(settings.getMockInterceptor())
-    );
+    ));
   }
 
   @Override
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/MockInstantiator.java b/spock-core/src/main/java/org/spockframework/mock/runtime/MockInstantiator.java
index bcb08f64ee..efb9fbfef5 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/MockInstantiator.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/MockInstantiator.java
@@ -25,7 +25,7 @@
 public class MockInstantiator {
   private static final boolean objenesisAvailable = ReflectionUtil.isClassAvailable("org.objenesis.Objenesis");
 
-  public static Object instantiate(Class declaredType, Class actualType, @Nullable List constructorArgs, boolean useObjenesis) {
+  public static  T instantiate(Class declaredType, Class actualType, @Nullable List constructorArgs, boolean useObjenesis) {
     try {
       if (!declaredType.isInterface() && constructorArgs == null && useObjenesis && objenesisAvailable) {
         return ObjenesisInstantiator.instantiate(actualType);
@@ -41,8 +41,8 @@ public static Object instantiate(Class declaredType, Class actualType, @Nu
 
   // inner class to defer class loading
   private static class ObjenesisInstantiator {
-    static Object instantiate(Class clazz) {
-      return clazz.cast(ObjenesisHelper.newInstance(clazz));
+    static  T instantiate(Class clazz) {
+      return ObjenesisHelper.newInstance(clazz);
     }
   }
 }
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/MockMakerRegistry.java b/spock-core/src/main/java/org/spockframework/mock/runtime/MockMakerRegistry.java
index d829d7fbb3..ea48a44b39 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/MockMakerRegistry.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/MockMakerRegistry.java
@@ -120,7 +120,7 @@ public List getMakerList() {
    * @return the new instance of the mocked type defined by the settings
    * @throws CannotCreateMockException if a mock with the provided settings can't be created.
    */
-  public Object makeMock(IMockCreationSettings settings) throws CannotCreateMockException {
+  public  T makeMock(IMockCreationSettings settings) throws CannotCreateMockException {
     if (settings.isStaticMock()) {
       throw new IllegalArgumentException("Can't mock static mocks with makeMock().");
     }
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/mockito/MockitoMockMaker.java b/spock-core/src/main/java/org/spockframework/mock/runtime/mockito/MockitoMockMaker.java
index 1506bba99c..a97d96e595 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/mockito/MockitoMockMaker.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/mockito/MockitoMockMaker.java
@@ -79,7 +79,7 @@ public IMockObject asMockOrNull(@Nullable Object object) {
   }
 
   @Override
-  public Object makeMock(IMockCreationSettings settings) throws CannotCreateMockException {
+  public  T makeMock(IMockCreationSettings settings) throws CannotCreateMockException {
     return Objects.requireNonNull(impl).makeMock(settings);
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/mockito/MockitoMockMakerImpl.java b/spock-core/src/main/java/org/spockframework/mock/runtime/mockito/MockitoMockMakerImpl.java
index d5ed388e21..b2e5b47553 100644
--- a/spock-core/src/main/java/org/spockframework/mock/runtime/mockito/MockitoMockMakerImpl.java
+++ b/spock-core/src/main/java/org/spockframework/mock/runtime/mockito/MockitoMockMakerImpl.java
@@ -101,7 +101,7 @@ public boolean isStaticMock(Class clazz) {
     return mockHandler instanceof SpockMockHandler;
   }
 
-  Object makeMock(IMockMaker.IMockCreationSettings settings) throws CannotCreateMockException {
+   T makeMock(IMockMaker.IMockCreationSettings settings) throws CannotCreateMockException {
     try {
       MockSettings mockitoSettings = Mockito.withSettings();
       mockitoSettings.mockMaker(INLINE);
@@ -120,7 +120,7 @@ Object makeMock(IMockMaker.IMockCreationSettings settings) throws CannotCreateMo
 
       applyMockMakerSettingsFromUser(settings, mockitoSettings);
 
-      MockCreationSettings mockitoCreationSettings = mockitoSettings.build(settings.getMockType());
+      MockCreationSettings mockitoCreationSettings = mockitoSettings.build(uncheckedCast(settings.getMockType()));
       SpockMockHandler handler = new SpockMockHandler(settings.getMockInterceptor());
 
       return inlineMockMaker.createMock(mockitoCreationSettings, handler);
diff --git a/spock-core/src/main/java/org/spockframework/runtime/ExpressionInfoConverter.java b/spock-core/src/main/java/org/spockframework/runtime/ExpressionInfoConverter.java
index 36c724e11d..7738d76a1a 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/ExpressionInfoConverter.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/ExpressionInfoConverter.java
@@ -142,8 +142,7 @@ public void visitVariableExpression(VariableExpression expr) {
         TextRegion.of(expr),
         TextPosition.startOf(expr),
         expr.getName()
-    ).setRelevant(expr != VariableExpression.THIS_EXPRESSION
-        && expr != VariableExpression.SUPER_EXPRESSION);
+    ).setRelevant(!expr.isThisExpression() && !expr.isSuperExpression());
   }
 
   @Override
diff --git a/spock-core/src/main/java/org/spockframework/runtime/GroovyRuntimeUtil.java b/spock-core/src/main/java/org/spockframework/runtime/GroovyRuntimeUtil.java
index 8b00cef824..e179d843d6 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/GroovyRuntimeUtil.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/GroovyRuntimeUtil.java
@@ -177,9 +177,9 @@ public static void setProperty(Object target, String property, Object value) {
   /**
    * Note: This method may throw checked exceptions although it doesn't say so.
    */
-  public static Object invokeConstructor(Class clazz, Object... args) {
+  public static  T invokeConstructor(Class clazz, Object... args) {
     try {
-      return InvokerHelper.invokeConstructorOf(clazz, args);
+      return clazz.cast(InvokerHelper.invokeConstructorOf(clazz, args));
     } catch (InvokerInvocationException e) {
       return ExceptionUtil.sneakyThrow(e.getCause());
     }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpecInternals.java b/spock-core/src/main/java/org/spockframework/runtime/SpecInternals.java
index 82cd07d6ae..45a47b4d6a 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpecInternals.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpecInternals.java
@@ -15,7 +15,7 @@
 package org.spockframework.runtime;
 
 import groovy.transform.stc.ClosureParams;
-import groovy.transform.stc.SecondParam;
+import groovy.transform.stc.FromString;
 import groovy.transform.stc.ThirdParam;
 import org.spockframework.mock.*;
 import org.spockframework.mock.runtime.*;
@@ -72,14 +72,14 @@ public static  T oldImpl(T expression) {
   }
 
   public static  T thrownImpl(Specification specification, String inferredName, Class inferredType) {
-    return inferredType.cast(checkExceptionThrown(specification, inferredType));
+    return checkExceptionThrown(specification, inferredType);
   }
 
-  public static Throwable thrownImpl(Specification specification, String inferredName, Class inferredType, Class specifiedType) {
+  public static  T thrownImpl(Specification specification, String inferredName, Class inferredType, Class specifiedType) {
     return checkExceptionThrown(specification, specifiedType == null ? inferredType : specifiedType);
   }
 
-  public static Throwable checkExceptionThrown(Specification specification, Class exceptionType) {
+  private static  T checkExceptionThrown(Specification specification, Class exceptionType) {
     if (exceptionType == null) {
       throw new InvalidSpecException("Thrown exception type cannot be inferred automatically. " +
         "Please specify a type explicitly (e.g. 'thrown(MyException)').");
@@ -92,7 +92,7 @@ public static Throwable checkExceptionThrown(Specification specification, Class<
 
     SpecificationContext specificationContext = uncheckedCast(specification.getSpecificationContext());
     Throwable actual = specificationContext.getThrownException();
-    if (exceptionType.isInstance(actual)) return actual;
+    if (exceptionType.isInstance(actual)) return exceptionType.cast(actual);
 
     throw new WrongExceptionThrownError(exceptionType, actual);
   }
@@ -101,7 +101,7 @@ public static  T MockImpl(Specification specification, String inferredName, T
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.JAVA, emptyMap(), null, null);
   }
 
-  public static  T MockImpl(Specification specification, String inferredName, Type inferredType, @ClosureParams(SecondParam.FirstGenericType.class) Closure closure) {
+  public static  T MockImpl(Specification specification, String inferredName, Type inferredType, @ClosureParams(ThirdParam.FirstGenericType.class) Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.JAVA, emptyMap(), null, closure);
   }
 
@@ -113,19 +113,19 @@ public static  T MockImpl(Specification specification, String inferredName, T
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.JAVA, options, null, closure);
   }
 
-  public static  T MockImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType) {
+  public static  T MockImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.JAVA, emptyMap(), specifiedType, null);
   }
 
-  public static  T MockImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType, Closure closure) {
+  public static  T MockImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType, Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.JAVA, emptyMap(), specifiedType, closure);
   }
 
-  public static  T MockImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType) {
+  public static  T MockImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.JAVA, options, specifiedType, null);
   }
 
-  public static  T MockImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType, Closure closure) {
+  public static  T MockImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType, Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.JAVA, options, specifiedType, closure);
   }
 
@@ -133,7 +133,7 @@ public static  T StubImpl(Specification specification, String inferredName, T
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.JAVA, emptyMap(), null, null);
   }
 
-  public static  T StubImpl(Specification specification, String inferredName, Type inferredType, @ClosureParams(SecondParam.FirstGenericType.class) Closure closure) {
+  public static  T StubImpl(Specification specification, String inferredName, Type inferredType, @ClosureParams(ThirdParam.FirstGenericType.class) Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.JAVA, emptyMap(), null, closure);
   }
 
@@ -145,19 +145,19 @@ public static  T StubImpl(Specification specification, String inferredName, T
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.JAVA, options, null, closure);
   }
 
-  public static  T StubImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType) {
+  public static  T StubImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.JAVA, emptyMap(), specifiedType, null);
   }
 
-  public static  T StubImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType, Closure closure) {
+  public static  T StubImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType, Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.JAVA, emptyMap(), specifiedType, closure);
   }
 
-  public static  T StubImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType) {
+  public static  T StubImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.JAVA, options, specifiedType, null);
   }
 
-  public static  T StubImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType, Closure closure) {
+  public static  T StubImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType, Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.JAVA, options, specifiedType, closure);
   }
 
@@ -165,7 +165,7 @@ public static  T SpyImpl(Specification specification, String inferredName, Ty
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.JAVA, emptyMap(), null, null);
   }
 
-  public static  T SpyImpl(Specification specification, String inferredName, Type inferredType, @ClosureParams(SecondParam.FirstGenericType.class) Closure closure) {
+  public static  T SpyImpl(Specification specification, String inferredName, Type inferredType, @ClosureParams(ThirdParam.FirstGenericType.class) Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.JAVA, emptyMap(), null, closure);
   }
 
@@ -177,19 +177,19 @@ public static  T SpyImpl(Specification specification, String inferredName, Ty
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.JAVA, options, null, closure);
   }
 
-  public static  T SpyImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType) {
+  public static  T SpyImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.JAVA, emptyMap(), specifiedType, null);
   }
 
-  public static  T SpyImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType, Closure closure) {
+  public static  T SpyImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType, Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.JAVA, emptyMap(), specifiedType, closure);
   }
 
-  public static  T SpyImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType) {
+  public static  T SpyImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.JAVA, options, specifiedType, null);
   }
 
-  public static  T SpyImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType, Closure closure) {
+  public static  T SpyImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType, Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.JAVA, options, specifiedType, closure);
   }
 
@@ -197,7 +197,7 @@ public static  T SpyImpl(Specification specification, String inferredName, Ty
     return SpyImpl(specification, inferredName, inferredType, instance, null);
   }
 
-  public static  T SpyImpl(Specification specification, String inferredName, Type inferredType, T instance, @ClosureParams(ThirdParam.class) Closure closure) {
+  public static  T SpyImpl(Specification specification, String inferredName, Type inferredType, T instance, @ClosureParams(value = FromString.class, options = "T") Closure closure) {
     if (instance == null) {
       throw new SpockException("Spy instance may not be null");
     }
@@ -211,7 +211,7 @@ public static  T GroovyMockImpl(Specification specification, String inferredN
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.GROOVY, emptyMap(), null, null);
   }
 
-  public static  T GroovyMockImpl(Specification specification, String inferredName, Type inferredType, @ClosureParams(SecondParam.FirstGenericType.class) Closure closure) {
+  public static  T GroovyMockImpl(Specification specification, String inferredName, Type inferredType, @ClosureParams(ThirdParam.FirstGenericType.class) Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.GROOVY, emptyMap(), null, closure);
   }
 
@@ -223,19 +223,19 @@ public static  T GroovyMockImpl(Specification specification, String inferredN
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.GROOVY, options, null, closure);
   }
 
-  public static  T GroovyMockImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType) {
+  public static  T GroovyMockImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.GROOVY, emptyMap(), specifiedType, null);
   }
 
-  public static  T GroovyMockImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType, Closure closure) {
+  public static  T GroovyMockImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType, Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.GROOVY, emptyMap(), specifiedType, closure);
   }
 
-  public static  T GroovyMockImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType) {
+  public static  T GroovyMockImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.GROOVY, options, specifiedType, null);
   }
 
-  public static  T GroovyMockImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType, Closure closure) {
+  public static  T GroovyMockImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType, Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.MOCK, MockImplementation.GROOVY, options, specifiedType, closure);
   }
 
@@ -243,7 +243,7 @@ public static  T GroovyStubImpl(Specification specification, String inferredN
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.GROOVY, emptyMap(), null, null);
   }
 
-  public static  T GroovyStubImpl(Specification specification, String inferredName, Type inferredType, @ClosureParams(SecondParam.FirstGenericType.class) Closure closure) {
+  public static  T GroovyStubImpl(Specification specification, String inferredName, Type inferredType, @ClosureParams(ThirdParam.FirstGenericType.class) Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.GROOVY, emptyMap(), null, closure);
   }
 
@@ -255,19 +255,19 @@ public static  T GroovyStubImpl(Specification specification, String inferredN
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.GROOVY, options, null, closure);
   }
 
-  public static  T GroovyStubImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType) {
+  public static  T GroovyStubImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.GROOVY, emptyMap(), specifiedType, null);
   }
 
-  public static  T GroovyStubImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType, Closure closure) {
+  public static  T GroovyStubImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType, Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.GROOVY, emptyMap(), specifiedType, closure);
   }
 
-  public static  T GroovyStubImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType) {
+  public static  T GroovyStubImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.GROOVY, options, specifiedType, null);
   }
 
-  public static  T GroovyStubImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType, Closure closure) {
+  public static  T GroovyStubImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType, Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.STUB, MockImplementation.GROOVY, options, specifiedType, closure);
   }
 
@@ -275,7 +275,7 @@ public static  T GroovySpyImpl(Specification specification, String inferredNa
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.GROOVY, emptyMap(), null, null);
   }
 
-  public static  T GroovySpyImpl(Specification specification, String inferredName, Type inferredType, @ClosureParams(SecondParam.FirstGenericType.class) Closure closure) {
+  public static  T GroovySpyImpl(Specification specification, String inferredName, Type inferredType, @ClosureParams(ThirdParam.FirstGenericType.class) Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.GROOVY, emptyMap(), null, closure);
   }
 
@@ -287,19 +287,19 @@ public static  T GroovySpyImpl(Specification specification, String inferredNa
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.GROOVY, options, null, closure);
   }
 
-  public static  T GroovySpyImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType) {
+  public static  T GroovySpyImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.GROOVY, emptyMap(), specifiedType, null);
   }
 
-  public static  T GroovySpyImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType, Closure closure) {
+  public static  T GroovySpyImpl(Specification specification, String inferredName, Type inferredType, Class specifiedType, Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.GROOVY, emptyMap(), specifiedType, closure);
   }
 
-  public static  T GroovySpyImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType) {
+  public static  T GroovySpyImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.GROOVY, options, specifiedType, null);
   }
 
-  public static  T GroovySpyImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType, Closure closure) {
+  public static  T GroovySpyImpl(Specification specification, String inferredName, Type inferredType, Map options, Class specifiedType, Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, MockNature.SPY, MockImplementation.GROOVY, options, specifiedType, closure);
   }
 
@@ -318,12 +318,12 @@ public static  T GroovySpyImpl(Specification specification, String inferredNa
   }
 
   private static  T createMockImpl(Specification specification, String inferredName, Type inferredType, MockNature nature,
-      MockImplementation implementation, Map options, Class specifiedType, Closure closure) {
+      MockImplementation implementation, Map options, Class specifiedType, Closure closure) {
     return createMockImpl(specification, inferredName, inferredType, null, nature, implementation, options, specifiedType, closure);
   }
 
   private static  T createMockImpl(Specification specification, String inferredName, Type inferredType, T instance, MockNature nature,
-      MockImplementation implementation, Map options, Class specifiedType, Closure closure) {
+      MockImplementation implementation, Map options, Class specifiedType, Closure closure) {
     Type effectiveType = specifiedType != null ? specifiedType : options.containsKey("type") ? (Type) options.get("type") : inferredType;
     if (effectiveType == null) {
       throw new InvalidSpecException(nature + " object type cannot be inferred automatically. " +
diff --git a/spock-core/src/main/java/org/spockframework/runtime/ValueRecorder.java b/spock-core/src/main/java/org/spockframework/runtime/ValueRecorder.java
index fbd4d04636..176fd8ef9f 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/ValueRecorder.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/ValueRecorder.java
@@ -41,8 +41,10 @@ public ValueRecorder reset() {
   /**
    * Records and returns the specified value. Hence an expression can be replaced
    * with record(expression) without impacting evaluation of the expression.
+   * The generic return type preserves the static type of the wrapped expression,
+   * allowing conditions to pass static type checking (e.g. under {@code @CompileStatic}).
    */
-  public Object record(int index, Object value) {
+  public  T record(int index, T value) {
     realizeNas(index + 1, null);
     values.set(index, value);
 
@@ -72,8 +74,9 @@ public int startRecordingValue(int index){
 
   /**
    * Materializes N/A values without recording a new value.
+   * Generic for the same reason as {@link #record(int, Object)}.
    */
-  public Object realizeNas(int index, Object value) {
+  public  T realizeNas(int index, T value) {
     for (int i = values.size(); i < index; i++)
       values.add(ExpressionInfo.VALUE_NOT_AVAILABLE);
     return value;
diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/IMethodInvocation.java b/spock-core/src/main/java/org/spockframework/runtime/extension/IMethodInvocation.java
index d2e56cd4d0..099e1317f3 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/extension/IMethodInvocation.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/extension/IMethodInvocation.java
@@ -18,6 +18,7 @@
 import org.spockframework.runtime.model.*;
 import org.spockframework.util.Beta;
 import org.spockframework.util.Nullable;
+import spock.lang.Specification;
 
 /**
  * @author Peter Niederwieser
@@ -60,7 +61,7 @@ public interface IMethodInvocation extends IStoreProvider {
    *
    * @return the {@code Specification} instance for {@code @Shared}fields
    */
-  Object getSharedInstance();
+  Specification getSharedInstance();
 
   /**
    * Returns the {@code Specification} instance for the current iteration.
@@ -69,7 +70,7 @@ public interface IMethodInvocation extends IStoreProvider {
    *
    * @return the {@code Specification} instance for the current iteration
    */
-  Object getInstance();
+  Specification getInstance();
 
   /**
    * Returns the target (receiver) of this method invocation.
diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/MethodInvocation.java b/spock-core/src/main/java/org/spockframework/runtime/extension/MethodInvocation.java
index c046190be5..bf30a5eadb 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/extension/MethodInvocation.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/extension/MethodInvocation.java
@@ -22,6 +22,7 @@
 import org.spockframework.runtime.model.SpecInfo;
 import org.spockframework.util.Checks;
 import org.spockframework.util.CollectionUtil;
+import spock.lang.Specification;
 
 import java.util.Iterator;
 import java.util.List;
@@ -35,8 +36,8 @@
 public class MethodInvocation implements IMethodInvocation {
   private final FeatureInfo feature;
   private final IterationInfo iteration;
-  private final Object sharedInstance;
-  private final Object instance;
+  private final Specification sharedInstance;
+  private final Specification instance;
   private final Object target;
   private final MethodInfo method;
   private Object[] arguments;
@@ -45,8 +46,8 @@ public class MethodInvocation implements IMethodInvocation {
 
   private final StoreProvider storeProvider;
 
-  public MethodInvocation(FeatureInfo feature, IterationInfo iteration, StoreProvider storeProvider, Object sharedInstance,
-                          Object instance, Object target, MethodInfo method, List scopedInterceptors, Object[] arguments) {
+  public MethodInvocation(FeatureInfo feature, IterationInfo iteration, StoreProvider storeProvider, Specification sharedInstance,
+                          Specification instance, Object target, MethodInfo method, List scopedInterceptors, Object[] arguments) {
     this.feature = feature;
     this.iteration = iteration;
     this.storeProvider = storeProvider;
@@ -60,8 +61,8 @@ public MethodInvocation(FeatureInfo feature, IterationInfo iteration, StoreProvi
       : CollectionUtil.concat(scopedInterceptors, method.getInterceptors()).iterator();
   }
 
-  private MethodInvocation(FeatureInfo feature, IterationInfo iteration, StoreProvider storeProvider, Object sharedInstance,
-                          Object instance, Object target, MethodInfo method, Iterator interceptors, Object[] arguments) {
+  private MethodInvocation(FeatureInfo feature, IterationInfo iteration, StoreProvider storeProvider, Specification sharedInstance,
+                           Specification instance, Object target, MethodInfo method, Iterator interceptors, Object[] arguments) {
     this.feature = feature;
     this.iteration = iteration;
     this.storeProvider = storeProvider;
@@ -89,12 +90,12 @@ public IterationInfo getIteration() {
   }
 
   @Override
-  public Object getSharedInstance() {
+  public Specification getSharedInstance() {
     return sharedInstance;
   }
 
   @Override
-  public Object getInstance() {
+  public Specification getInstance() {
     return instance;
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/ConditionalExtension.java b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/ConditionalExtension.java
index 972a808815..7d4d148df0 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/ConditionalExtension.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/ConditionalExtension.java
@@ -25,6 +25,7 @@
 import org.spockframework.runtime.extension.builtin.PreconditionContext.*;
 import org.spockframework.runtime.model.FeatureInfo;
 import org.spockframework.runtime.model.SpecInfo;
+import spock.lang.Specification;
 
 import java.lang.annotation.Annotation;
 import java.util.Map;
@@ -32,7 +33,7 @@
 import static java.util.Collections.emptyMap;
 
 public abstract class ConditionalExtension implements IAnnotationDrivenExtension {
-  protected abstract Class getConditionClass(T annotation);
+  protected abstract Class> getConditionClass(T annotation);
 
   protected void specConditionResult(boolean result, T annotation, SpecInfo spec) {
     throw new UnsupportedOperationException();
@@ -97,8 +98,8 @@ public void visitFeatureAnnotation(T annotation, FeatureInfo feature) {
     }
   }
 
-  private Closure createCondition(T annotation) {
-    Class clazz = getConditionClass(annotation);
+  private Closure createCondition(T annotation) {
+    Class> clazz = getConditionClass(annotation);
     try {
       return clazz.getConstructor(Object.class, Object.class).newInstance(null, null);
     } catch (Exception e) {
@@ -106,22 +107,22 @@ private Closure createCondition(T annotation) {
     }
   }
 
-  private static Object evaluateCondition(Closure condition,
-                                          Object sharedInstance,
-                                          Object instance,
+  private static Object evaluateCondition(Closure condition,
+                                          Specification sharedInstance,
+                                          Specification instance,
                                           Map dataVariables) {
     return evaluateCondition(condition, sharedInstance, instance, dataVariables, null);
   }
 
-  private static Object evaluateCondition(Closure condition, Object owner) {
+  private static Object evaluateCondition(Closure condition, Object owner) {
     return evaluateCondition(condition, null, null, emptyMap(), owner);
   }
 
-  private static Object evaluateCondition(Closure condition,
-                                          Object sharedInstance,
-                                          Object instance,
+  private static Object evaluateCondition(Closure condition,
+                                          Specification sharedInstance,
+                                          Specification instance,
                                           Map dataVariables, Object owner) {
-    PreconditionContext context = new PreconditionContext(sharedInstance, instance, dataVariables);
+    PreconditionContext context = new PreconditionContext<>(sharedInstance, instance, dataVariables);
     condition = condition.rehydrate(context, owner, null);
     condition.setResolveStrategy(Closure.DELEGATE_FIRST);
 
diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/IgnoreIfExtension.java b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/IgnoreIfExtension.java
index 011a6cd9fe..90a13faf4d 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/IgnoreIfExtension.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/IgnoreIfExtension.java
@@ -36,7 +36,7 @@ public class IgnoreIfExtension extends ConditionalExtension implements
   private static final String DEFAULT_MESSAGE = "Ignored via @" + IgnoreIf.class.getSimpleName();
 
   @Override
-  protected Class getConditionClass(IgnoreIf annotation) {
+  protected Class> getConditionClass(IgnoreIf annotation) {
     return annotation.value();
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/PendingFeatureIfExtension.java b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/PendingFeatureIfExtension.java
index 4aca691b0b..0e307fda27 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/PendingFeatureIfExtension.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/PendingFeatureIfExtension.java
@@ -11,7 +11,7 @@ public class PendingFeatureIfExtension extends ConditionalExtension getConditionClass(PendingFeatureIf annotation) {
+  protected Class> getConditionClass(PendingFeatureIf annotation) {
     return annotation.value();
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/PreconditionContext.java b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/PreconditionContext.java
index cad33cc740..c03db30708 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/PreconditionContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/PreconditionContext.java
@@ -17,10 +17,12 @@
 package org.spockframework.runtime.extension.builtin;
 
 import static java.util.Collections.emptyMap;
+import static org.spockframework.util.ObjectUtil.uncheckedCast;
 
 import java.math.BigDecimal;
 import java.util.*;
 
+import org.spockframework.util.ObjectUtil;
 import spock.lang.IgnoreIf;
 import spock.lang.PendingFeatureIf;
 import spock.lang.Requires;
@@ -32,15 +34,15 @@
  * The context (delegate) for a {@link Requires}, {@link IgnoreIf} or {@link PendingFeatureIf} condition.
  */
 public class PreconditionContext {
-  private final Object theSharedInstance;
-  private final Object theInstance;
+  private final S theSharedInstance;
+  private final S theInstance;
   private final Map dataVariables;
 
   public PreconditionContext() {
     this(null, null, emptyMap());
   }
 
-  public PreconditionContext(Object sharedInstance, Object instance, Map dataVariables) {
+  public PreconditionContext(S sharedInstance, S instance, Map dataVariables) {
     this.theSharedInstance = sharedInstance;
     this.theInstance = instance;
     this.dataVariables = new StrictHashMap<>(dataVariables);
@@ -93,7 +95,7 @@ public S getInstance() {
     if (theInstance == null) {
       throw new InstanceContextException();
     }
-    return (S) theInstance;
+    return uncheckedCast(theInstance);
   }
 
   /**
@@ -108,7 +110,7 @@ public S getShared() {
     if (theSharedInstance == null) {
       throw new SharedContextException();
     }
-    return (S) theSharedInstance;
+    return uncheckedCast(theSharedInstance);
   }
 
   /**
diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/RequiresExtension.java b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/RequiresExtension.java
index 10b46f11a5..ea96d8ff12 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/RequiresExtension.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/RequiresExtension.java
@@ -36,7 +36,7 @@ public class RequiresExtension extends ConditionalExtension implements
   private static final String DEFAULT_MESSAGE = "Ignored via @" + Requires.class.getSimpleName();
 
   @Override
-  protected Class getConditionClass(Requires annotation) {
+  protected Class> getConditionClass(Requires annotation) {
     return annotation.value();
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/RetryBaseInterceptor.java b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/RetryBaseInterceptor.java
index 24959bcccd..a0c45f159c 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/RetryBaseInterceptor.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/RetryBaseInterceptor.java
@@ -25,6 +25,7 @@
 
 import groovy.lang.Closure;
 import org.opentest4j.MultipleFailuresError;
+import spock.lang.Specification;
 
 /**
  * @author Leonard Brünings
@@ -77,14 +78,15 @@ private boolean satisfiesCondition(IMethodInvocation invocation, Throwable failu
     if (condition == null) {
       return true;
     }
-    final Closure condition = this.condition.rehydrate(
-      new RetryConditionContext(invocation.getInstance(), failure),
+    RetryConditionContext retryConditionContext = new RetryConditionContext<>(invocation.getInstance(), failure);
+    final Closure condition = this.condition.rehydrate(
+      retryConditionContext,
       invocation.getSpec().getReflection(),
       null);
     condition.setResolveStrategy(Closure.DELEGATE_FIRST);
 
     try {
-      return GroovyRuntimeUtil.isTruthy(condition.call());
+      return GroovyRuntimeUtil.isTruthy(condition.call(retryConditionContext));
     } catch (Exception e) {
       throw new ExtensionException("Failed to evaluate @Retry condition", e);
     }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/RetryConditionContext.java b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/RetryConditionContext.java
index ed9d9a57f1..36407318f0 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/RetryConditionContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/extension/builtin/RetryConditionContext.java
@@ -7,10 +7,10 @@
  */
 public class RetryConditionContext {
 
-  private final Object instance;
+  private final S instance;
   private final Throwable failure;
 
-  RetryConditionContext(Object instance, Throwable failure) {
+  RetryConditionContext(S instance, Throwable failure) {
     this.instance = instance;
     this.failure = failure;
   }
@@ -30,7 +30,7 @@ public Throwable getFailure() {
    * @return the current {@code Specification} instance
    */
   public S getInstance() {
-    return (S) instance;
+    return instance;
   }
 
 }
diff --git a/spock-core/src/main/java/spock/lang/IgnoreIf.java b/spock-core/src/main/java/spock/lang/IgnoreIf.java
index 1db7c917ba..f57f499130 100644
--- a/spock-core/src/main/java/spock/lang/IgnoreIf.java
+++ b/spock-core/src/main/java/spock/lang/IgnoreIf.java
@@ -59,7 +59,7 @@
    *
    * @return the closure to evaluate
    */
-  Class value();
+  Class> value();
 
   /**
    * Whether this annotation should be inherited by child classes.
diff --git a/spock-core/src/main/java/spock/lang/PendingFeatureIf.java b/spock-core/src/main/java/spock/lang/PendingFeatureIf.java
index 3342ca9d81..c2a635e99f 100644
--- a/spock-core/src/main/java/spock/lang/PendingFeatureIf.java
+++ b/spock-core/src/main/java/spock/lang/PendingFeatureIf.java
@@ -38,7 +38,7 @@
    *
    * @return Closure to be evaluated
    */
-  Class value();
+  Class> value();
 
   /**
    * Configures which types of Exceptions are expected in the pending feature.
diff --git a/spock-core/src/main/java/spock/lang/Requires.java b/spock-core/src/main/java/spock/lang/Requires.java
index f67aa05e3e..b0d9111a89 100644
--- a/spock-core/src/main/java/spock/lang/Requires.java
+++ b/spock-core/src/main/java/spock/lang/Requires.java
@@ -59,7 +59,7 @@
    *
    * @return the closure to evaluate
    */
-  Class value();
+  Class> value();
 
   /**
    * Whether this annotation should be inherited by child classes.
diff --git a/spock-core/src/main/java/spock/lang/SpockCompileDynamic.java b/spock-core/src/main/java/spock/lang/SpockCompileDynamic.java
new file mode 100644
index 0000000000..c3c92f43a8
--- /dev/null
+++ b/spock-core/src/main/java/spock/lang/SpockCompileDynamic.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2026 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package spock.lang;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+@Documented
+@Retention(SOURCE)
+@Target({METHOD, TYPE, CONSTRUCTOR})
+public @interface SpockCompileDynamic {
+}
diff --git a/spock-core/src/main/java/spock/lang/SpockCompileStatic.java b/spock-core/src/main/java/spock/lang/SpockCompileStatic.java
new file mode 100644
index 0000000000..8205a2a226
--- /dev/null
+++ b/spock-core/src/main/java/spock/lang/SpockCompileStatic.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2026 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package spock.lang;
+
+import groovy.transform.TypeCheckingMode;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static groovy.transform.TypeCheckingMode.PASS;
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+@Documented
+@Retention(SOURCE)
+@Target({METHOD, TYPE, CONSTRUCTOR})
+public @interface SpockCompileStatic {
+  TypeCheckingMode value() default PASS;
+
+  String[] extensions() default {};
+}
diff --git a/spock-core/src/main/java/spock/lang/SpockTypeChecked.java b/spock-core/src/main/java/spock/lang/SpockTypeChecked.java
new file mode 100644
index 0000000000..f5ef4b120e
--- /dev/null
+++ b/spock-core/src/main/java/spock/lang/SpockTypeChecked.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2026 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package spock.lang;
+
+import groovy.transform.TypeCheckingMode;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static groovy.transform.TypeCheckingMode.PASS;
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+@Documented
+@Retention(SOURCE)
+@Target({METHOD, TYPE, CONSTRUCTOR})
+public @interface SpockTypeChecked {
+  TypeCheckingMode value() default PASS;
+
+  String[] extensions() default {};
+}
diff --git a/spock-core/src/main/java/spock/mock/DetachedMockFactory.java b/spock-core/src/main/java/spock/mock/DetachedMockFactory.java
index 37b600525d..0dfe602187 100644
--- a/spock-core/src/main/java/spock/mock/DetachedMockFactory.java
+++ b/spock-core/src/main/java/spock/mock/DetachedMockFactory.java
@@ -162,7 +162,7 @@ public  T createMock(@Nullable String name, Class type, MockNature nature,
     if (classLoader == null) {
       classLoader = ClassLoader.getSystemClassLoader();
     }
-    return (T) new MockUtil().createDetachedMock(name, type, nature, MockImplementation.JAVA, options, classLoader);
+    return new MockUtil().createDetachedMock(name, type, nature, MockImplementation.JAVA, options, classLoader);
   }
 
   @SuppressWarnings("unchecked")
@@ -171,7 +171,7 @@ public  T createMock(@Nullable String name, T obj, MockNature nature, Map type) {
diff --git a/spock-core/src/main/resources/META-INF/services/org.codehaus.groovy.transform.ASTTransformation b/spock-core/src/main/resources/META-INF/services/org.codehaus.groovy.transform.ASTTransformation
index 85763d3b3c..9be746349b 100644
--- a/spock-core/src/main/resources/META-INF/services/org.codehaus.groovy.transform.ASTTransformation
+++ b/spock-core/src/main/resources/META-INF/services/org.codehaus.groovy.transform.ASTTransformation
@@ -1 +1,2 @@
-org.spockframework.compiler.SpockTransform
\ No newline at end of file
+org.spockframework.compiler.SpockTransform
+org.spockframework.compiler.MarkAnnotationsAlreadyPresentInConversionPhase
diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/CompileStaticSupport.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/CompileStaticSupport.groovy
new file mode 100644
index 0000000000..713de8603c
--- /dev/null
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/CompileStaticSupport.groovy
@@ -0,0 +1,803 @@
+/*
+ * Copyright 2026 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.spockframework.smoke
+
+import groovy.transform.CompileDynamic
+import groovy.transform.CompileStatic
+import groovy.transform.TypeChecked
+import org.codehaus.groovy.control.CompilationFailedException
+import org.codehaus.groovy.syntax.SyntaxException
+import org.spockframework.EmbeddedSpecification
+import org.spockframework.runtime.ConditionNotSatisfiedError
+import org.spockframework.runtime.GroovyRuntimeUtil
+import org.spockframework.runtime.InvalidSpecException
+import org.spockframework.runtime.SpockComparisonFailure
+import org.spockframework.runtime.WrongExceptionThrownError
+import spock.lang.PendingFeatureIf
+import spock.lang.SpockCompileDynamic
+import spock.lang.SpockCompileStatic
+import spock.lang.SpockTypeChecked
+
+/**
+ * Specs annotated with {@code @CompileStatic} must compile even though
+ * Spock rewrites their conditions, and must render failed conditions
+ * with the recorded values.
+ */
+class CompileStaticSupport extends EmbeddedSpecification {
+  def "comparison conditions on typed variables compile with @#annotation.simpleName"() {
+    when:
+    compiler.compileWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "comparisons"() {
+    given:
+    int x = 42
+
+    expect:
+    x > 40
+    x >= 42
+    x < 43
+    x <= 42
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "property access in conditions compiles with @#annotation.simpleName"() {
+    given:
+    compiler.addClassImport(CompileStatic)
+
+    when:
+    compiler.compileWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "property access"() {
+    given:
+    Pogo pogo = new Pogo(name: "Fred")
+
+    expect:
+    pogo.name == "Fred"
+  }
+}
+
+@CompileStatic
+class Pogo {
+  String name
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  @PendingFeatureIf(
+    value = { GroovyRuntimeUtil.MAJOR_VERSION >= 5 },
+    reason = 'https://issues.apache.org/jira/browse/GROOVY-12456'
+  )
+  def "Map-construction with explicit map syntax compiles in assertions with @#annotation.simpleName"() {
+    given:
+    compiler.addClassImport(CompileStatic)
+
+    when:
+    compiler.compileWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "property access"() {
+    expect:
+    new Pogo([name: "Fred"])
+  }
+}
+
+@CompileStatic
+class Pogo {
+  String name
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  @PendingFeatureIf(
+    value = { GroovyRuntimeUtil.MAJOR_VERSION >= 5 },
+    reason = 'https://issues.apache.org/jira/browse/GROOVY-12456'
+  )
+  def "Map-construction with implicit map syntax compiles in assertions with @#annotation.simpleName"() {
+    given:
+    compiler.addClassImport(CompileStatic)
+
+    when:
+    compiler.compileWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "property access"() {
+    expect:
+    new Pogo(name: "Fred")
+  }
+}
+
+@CompileStatic
+class Pogo {
+  String name
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "@#annotation.simpleName is not allowed on non-specification classes"() {
+    when:
+    compiler.compileWithImports """
+@${annotation.name}
+class Pogo {
+  String name
+}
+"""
+
+    then:
+    SyntaxException e = thrown()
+    e.message.contains('SpockCompileDynamic, SpockCompileStatic, and SpockTypeChecked annotations are not allowed outside Spock specifications, use CompileDynamic, CompileStatic, or TypeChecked instead')
+
+    where:
+    annotation << [SpockCompileDynamic, SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "@#annotation.simpleName is not allowed on non-specification methods"() {
+    when:
+    compiler.compileWithImports """
+class Pogo {
+  String name
+  @${annotation.name}
+  def foo() {
+  }
+}
+"""
+
+    then:
+    SyntaxException e = thrown()
+    e.message.contains('SpockCompileDynamic, SpockCompileStatic, and SpockTypeChecked annotations are not allowed outside Spock specifications, use CompileDynamic, CompileStatic, or TypeChecked instead')
+
+    where:
+    annotation << [SpockCompileDynamic, SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "@#annotation.simpleName is not allowed on specification classes"() {
+    given:
+    compiler.addClassImport(CompileStatic)
+
+    when:
+    compiler.compileWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "property access"() {
+    given:
+    Pogo pogo = new Pogo(name: "Fred")
+
+    expect:
+    pogo.name == "Fred"
+  }
+}
+
+@CompileStatic
+class Pogo {
+  String name
+}
+"""
+
+    then:
+    SyntaxException e = thrown()
+    e.message.contains('CompileDynamic, CompileStatic, and TypeChecked annotations are not allowed within Spock specifications as the IDE would majorly misreport type problems, use SpockCompileDynamic, SpockCompileStatic, or SpockTypeChecked instead')
+
+    where:
+    annotation << [CompileDynamic, TypeChecked, CompileStatic]
+  }
+
+  def "@#annotation.simpleName is not allowed on specification methods"() {
+    given:
+    compiler.addClassImport(CompileStatic)
+
+    when:
+    compiler.compileWithImports """
+class ASpec extends Specification {
+  @${annotation.name}
+  def "property access"() {
+    given:
+    Pogo pogo = new Pogo(name: "Fred")
+
+    expect:
+    pogo.name == "Fred"
+  }
+}
+
+@CompileStatic
+class Pogo {
+  String name
+}
+"""
+
+    then:
+    SyntaxException e = thrown()
+    e.message.contains('CompileDynamic, CompileStatic, and TypeChecked annotations are not allowed within Spock specifications as the IDE would majorly misreport type problems, use SpockCompileDynamic, SpockCompileStatic, or SpockTypeChecked instead')
+
+    where:
+    annotation << [CompileDynamic, TypeChecked, CompileStatic]
+  }
+
+  def "indexing in conditions compiles with @#annotation.simpleName"() {
+    when:
+    compiler.compileWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "indexing"() {
+    given:
+    List list = ['a']
+
+    expect:
+    list[0] == 'a'
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "top-level method call conditions compile with @#annotation.simpleName"() {
+    when:
+    compiler.compileWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "method call condition"() {
+    given:
+    List list = ['a']
+
+    expect:
+    list.contains('a')
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "old() compiles and evaluates with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "old"() {
+    given:
+    List list = ['a']
+    int before = list.size()
+
+    when:
+    list.add('b')
+    int after = list.size()
+
+    then:
+    after == old(before) + 1
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "data table cells referencing previous columns compile with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "previous column reference"(int a, int b) {
+    when:
+    int expected = a + 1
+
+    then:
+    b == expected
+
+    where:
+    a | b
+    1 | a + 1
+    5 | a + 1
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "interactions in mock init closures work with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "mock init closure"() {
+    given:
+    List list = Mock(List) {
+      size() >> 3
+    }
+
+    when:
+    int size = list.size()
+
+    then:
+    size == 3
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "non-existent constant-name methods in interactions fail compilation with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "mock init closure"() {
+    given:
+    List list = Mock(List) { List it ->
+      it.does_not_exist() >> 3
+    }
+
+    when:
+    int size = list.size()
+
+    then:
+    size == 0
+  }
+}
+"""
+
+    then:
+    CompilationFailedException e = thrown()
+    e.message.contains('does_not_exist')
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "non-existent method in where block fails compilation with @#annotation.simpleName on class"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "type-checked where block"() {
+    expect:
+    true
+
+    where:
+    foo = [].does_not_exist()
+  }
+}
+"""
+
+    then:
+    CompilationFailedException e = thrown()
+    e.message.contains('does_not_exist')
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "non-existent method in where block fails compilation with @#annotation.simpleName on method"() {
+    when:
+    runner.runWithImports """
+class ASpec extends Specification {
+  @${annotation.name}
+  def "type-checked where block"() {
+    expect:
+    true
+
+    where:
+    foo = [].does_not_exist()
+  }
+}
+"""
+
+    then:
+    CompilationFailedException e = thrown()
+    e.message.contains('does_not_exist')
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "interactions in interaction blocks with argument-inferred variable type work with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "interaction block"() {
+    given:
+    def list = Mock(List)
+
+    when:
+    list.add("x")
+
+    then:
+    interaction {
+      1 * list.add(*_)
+    }
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "interactions in interaction blocks work with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "interaction block"() {
+    given:
+    List list = Mock()
+
+    when:
+    list.add("x")
+
+    then:
+    interaction {
+      1 * list.add(*_)
+    }
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "method calls in conditions compile with @#annotation.simpleName"() {
+    when:
+    compiler.compileWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "method call conditions"() {
+    given:
+    List list = ['a', 'b']
+
+    expect:
+    list.size() == 2
+    !list.isEmpty()
+    list.first().toUpperCase() == 'A'
+    this.helper(1) == 2
+    helper(1) == 2
+  }
+
+  int helper(int i) {
+    i + 1
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "method-level CompileStatic annotation is honored with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+class ASpec extends Specification {
+  @${annotation.name}
+  def "statically checked feature"() {
+    given:
+    List list = ['a']
+
+    expect:
+    list.size() == 1
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "exception property access in conditions compiles with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "thrown message"() {
+    when:
+    throw new IllegalStateException("bam")
+
+    then:
+    IllegalStateException e = thrown()
+    e.message == "bam"
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "unclear exception type compiles but fails at runtime with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "thrown message"() {
+    when:
+    throw new IllegalStateException("bam")
+
+    then:
+    def e = thrown(null)
+    e.message == "bam"
+  }
+}
+"""
+
+    then:
+    InvalidSpecException e = thrown()
+    e.message == "Thrown exception type cannot be inferred automatically. Please specify a type explicitly (e.g. 'thrown(MyException)')."
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "exception property access from inferred variable type compiles with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "thrown message"() {
+    when:
+    throw new IllegalStateException("bam")
+
+    then:
+    def e = thrown(IllegalStateException)
+    e.message == "bam"
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "exception property access with explicit type and null argument compiles with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "thrown message"() {
+    when:
+    throw new IllegalStateException("bam")
+
+    then:
+    IllegalStateException e = thrown(null)
+    e.message == "bam"
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "exception assertion with unrelated types should not compile with @#annotation.simpleName"() {
+    when:
+    compiler.compileWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "thrown message"() {
+    when:
+    throw new IllegalStateException("bam")
+
+    then:
+    UnsupportedOperationException e = thrown(IllegalStateException)
+    e.message == "bam"
+  }
+}
+"""
+
+    then:
+    SyntaxException e = thrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "exception assertion with compatible types but wrong thrown should fail accordingly with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "thrown message"() {
+    when:
+    throw new IllegalStateException("bam")
+
+    then:
+    RuntimeException e = thrown(UnsupportedOperationException)
+    e.message == "bam"
+  }
+}
+"""
+
+    then:
+    WrongExceptionThrownError e = thrown()
+    e.message == "Expected exception of type 'java.lang.UnsupportedOperationException', but got 'java.lang.IllegalStateException'"
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "exception assertion with incompatible types should not compile with @#annotation.simpleName"() {
+    when:
+    compiler.compileWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "thrown message"() {
+    when:
+    throw new IllegalStateException("bam")
+
+    then:
+    UnsupportedOperationException e = thrown(RuntimeException)
+    e.message == "bam"
+  }
+}
+"""
+
+    then:
+    SyntaxException e = thrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "method calls on the delegate of with blocks compile with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "with block"() {
+    given:
+    List list = ['a', 'b']
+
+    expect:
+    with(list) {
+      size() == 2
+      first() == 'a'
+    }
+  }
+}
+"""
+
+    then:
+    noExceptionThrown()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "failed method call condition renders recorded values with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "method call"() {
+    given:
+    List list = ['a']
+
+    expect:
+    list.size() == 2
+  }
+}
+"""
+
+    then:
+    SpockComparisonFailure e = thrown()
+    e.condition.rendering.trim() == """
+list.size() == 2
+|    |      |
+[a]  1      false
+""".trim()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+
+  def "failed comparison condition renders recorded values with @#annotation.simpleName"() {
+    when:
+    runner.runWithImports """
+@${annotation.name}
+class ASpec extends Specification {
+  def "comparison"() {
+    given:
+    int x = 42
+
+    expect:
+    x > 43
+  }
+}
+"""
+
+    then:
+    ConditionNotSatisfiedError e = thrown()
+    e.condition.rendering.trim() == """
+x > 43
+| |
+| false
+42
+""".trim()
+
+    where:
+    annotation << [SpockTypeChecked, SpockCompileStatic]
+  }
+}
diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/StaticTypeChecking.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/StaticTypeChecking.groovy
index bb7af932c9..43ebec06c3 100644
--- a/spock-specs/src/test/groovy/org/spockframework/smoke/StaticTypeChecking.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/StaticTypeChecking.groovy
@@ -24,7 +24,7 @@ class StaticTypeChecking extends EmbeddedSpecification {
   def "correctly spelled setup compiles successfully"() {
     when:
     compiler.compileSpecBody("""
-@groovy.transform.TypeChecked
+@spock.lang.SpockTypeChecked
 def foo(Object element){
   assert element != null
 }""")
diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/WithBlocksStatic.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/WithBlocksStatic.groovy
index cf71cb884c..7b25b4e3c0 100644
--- a/spock-specs/src/test/groovy/org/spockframework/smoke/WithBlocksStatic.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/WithBlocksStatic.groovy
@@ -18,8 +18,9 @@ package org.spockframework.smoke
 import spock.lang.Specification
 
 import groovy.transform.CompileStatic
+import spock.lang.SpockCompileStatic
 
-@CompileStatic
+@SpockCompileStatic
 class WithBlocksStatic extends Specification {
 
   def "can statically compile against type of the target"() {
diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/AstSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/AstSpec.groovy
index 90a0d1c09b..30ae592b30 100644
--- a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/AstSpec.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/AstSpec.groovy
@@ -24,6 +24,9 @@ import org.spockframework.util.VersionNumber
 import spock.lang.Requires
 import spock.lang.Snapshot
 import spock.lang.Snapshotter
+import spock.lang.SpockCompileDynamic
+import spock.lang.SpockCompileStatic
+import spock.lang.SpockTypeChecked
 import spock.util.Show
 
 class AstSpec extends EmbeddedSpecification {
@@ -136,6 +139,33 @@ class Foo {
     snapshotter.assertThat(result.source).matchesSnapshot()
   }
 
+  def "STC annotations are transplanted to generated methods with @#annotation.simpleName"() {
+    when:
+    def result = compiler.transpileWithImports("""
+class ASpec extends Specification {
+  @${annotation.name}
+  def "type-checked where block"() {
+    expect:
+    true
+
+    where:
+    final foo = [].does_not_exist()
+    bar = [].does_not_exist()
+    baz << [[].does_not_exist()]
+    combined:
+    bam << [[].does_not_exist()]
+    filter:
+    [[].does_not_exist()]
+  }
+}
+""")
+    then:
+    snapshotter.assertThat(result.source).matchesSnapshot(GroovyRuntimeUtil.MAJOR_VERSION >= 3 ? "" : "groovy25")
+
+    where:
+    annotation << [SpockCompileDynamic, SpockTypeChecked, SpockCompileStatic]
+  }
+
   def "nested generic type arguments are rendered faithfully"() {
     when:
     def result = compiler.transpile('''
diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/condition/CompileStaticConditionRendering.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/condition/CompileStaticConditionRendering.groovy
new file mode 100644
index 0000000000..1767e67544
--- /dev/null
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/condition/CompileStaticConditionRendering.groovy
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2026 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.spockframework.smoke.condition
+
+import spock.lang.SpockCompileStatic
+
+/**
+ * Describes rendering of conditions in statically compiled code.
+ * The rendered output must match the one of dynamic code, see {@link ConditionRendering}.
+ */
+@SpockCompileStatic
+class CompileStaticConditionRendering extends ConditionRenderingSpec {
+  def "simple condition"() {
+    expect:
+    isRendered """
+x == 1
+| |
+2 false
+    """, {
+      int x = 2
+      assert x == 1
+    }
+  }
+
+  private int one(int x) { 0 }
+
+  def "MethodCallExpression with implicit target"() {
+    expect:
+    isRendered """
+one(a)
+|   |
+0   1
+    """, {
+      int a = 1
+      assert one(a)
+    }
+  }
+
+  def "MethodCallExpression with explicit target"() {
+    expect:
+    isRendered """
+a.get(b) == null
+| |   |  |
+| 1   0  false
+[1]
+    """, {
+      List a = [1]
+      int b = 0
+      assert a.get(b) == null
+    }
+  }
+
+  def "MethodCallExpression invoking static method"() {
+    expect:
+    // unlike in dynamic code, the target class of a static method call is not
+    // rendered, because statically compiled code never evaluates the receiver
+    // expression of a static method call, so its value is never recorded
+    isRendered """
+Math.max(a,b) == c
+     |   | |  |  |
+     2   1 2  |  0
+              false
+    """, {
+      int a = 1
+      int b = 2
+      int c = 0
+      assert Math.max(a,b) == c
+    }
+  }
+
+  def "MethodCallExpression with spread-dot operator"() {
+    expect:
+    isRendered """
+["1", "22"]*.size() == null
+             |      |
+             [1, 2] false
+    """, {
+      assert ["1", "22"]*.size() == null
+    }
+  }
+
+  def "MethodCallExpression with safe operator"() {
+    expect:
+    isRendered """
+a?.length()
+|  |
+|  null
+null
+    """, {
+      String a = null
+      assert a?.length()
+    }
+  }
+
+  def "top-level MethodCallExpression"() {
+    expect:
+    isRendered """
+a.contains(b)
+| |        |
+| false    2
+[0]
+    """, {
+      List a = [0]
+      int b = 2
+      assert a.contains(b)
+    }
+  }
+
+  def "chained MethodCallExpressions"() {
+    expect:
+    isRendered """
+a.first().toUpperCase() == "B"
+| |       |             |
+| a       A             false
+[a]                     1 difference (0% similarity)
+                        (A)
+                        (B)
+    """, {
+      List a = ["a"]
+      assert a.first().toUpperCase() == "B"
+    }
+  }
+
+  def "TernaryExpression"() {
+    expect:
+    isRendered """
+a ? b : c
+|   |
+1   0
+    """, {
+      int a = 1
+      int b = 0
+      int c = 1
+      assert a ? b : c
+    }
+  }
+
+  def "BinaryExpression"() {
+    expect:
+    isRendered """
+a * b
+| | |
+0 0 1
+    """, {
+      int a = 0
+      int b = 1
+      assert a * b
+    }
+
+    isRendered """
+a[b]
+|||
+||0
+|false
+[false]
+    """, {
+      List a = [false]
+      int b = 0
+      assert a[b]
+    }
+  }
+
+  def "comparison operators"() {
+    expect:
+    isRendered """
+x > 43
+| |
+| false
+42
+    """, {
+      int x = 42
+      assert x > 43
+    }
+  }
+
+  def "BooleanExpression"() {
+    expect:
+    isRendered """
+a
+|
+null
+    """, {
+      String a = null
+      assert a
+    }
+  }
+
+  def "PropertyExpression"() {
+    expect:
+    isRendered """
+a.empty == true
+| |     |
+| false false
+[9]
+    """, {
+      List a = [9]
+      assert a.empty == true
+    }
+  }
+
+  def "instanceof expression"() {
+    expect:
+    isRendered """
+x instanceof Integer
+| |          |
+| false      class java.lang.Integer
+foo (java.lang.String)
+    """, {
+      Object x = "foo"
+      assert x instanceof Integer
+    }
+  }
+
+  def "VariableExpression"() {
+    expect:
+    isRendered """
+x
+|
+0
+    """, {
+      Integer x = 0
+      assert x
+    }
+  }
+
+  def "GStringExpression"() {
+    expect:
+    isRendered '''
+"$a and ${b + c}" == null
+  |       | | |   |
+  1       2 5 3   false
+    ''', {
+      int a = 1
+      int b = 2
+      int c = 3
+      assert "$a and ${b + c}" == null
+    }
+  }
+
+  def "condition in with block"() {
+    expect:
+    isRendered """
+size() == 3
+|      |
+2      false
+    """, {
+      List list = ["a", "b"]
+      with(list) {
+        assert size() == 3
+      }
+    }
+  }
+}
diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/condition/TypeCheckedConditionRendering.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/condition/TypeCheckedConditionRendering.groovy
new file mode 100644
index 0000000000..65ffde25a8
--- /dev/null
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/condition/TypeCheckedConditionRendering.groovy
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2026 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.spockframework.smoke.condition
+
+import spock.lang.SpockTypeChecked
+
+/**
+ * Describes rendering of conditions in statically compiled code.
+ * The rendered output must match the one of dynamic code, see {@link ConditionRendering}.
+ */
+@SpockTypeChecked
+class TypeCheckedConditionRendering extends ConditionRenderingSpec {
+  def "simple condition"() {
+    expect:
+    isRendered """
+x == 1
+| |
+2 false
+    """, {
+      int x = 2
+      assert x == 1
+    }
+  }
+
+  private int one(int x) { 0 }
+
+  def "MethodCallExpression with implicit target"() {
+    expect:
+    isRendered """
+one(a)
+|   |
+0   1
+    """, {
+      int a = 1
+      assert one(a)
+    }
+  }
+
+  def "MethodCallExpression with explicit target"() {
+    expect:
+    isRendered """
+a.get(b) == null
+| |   |  |
+| 1   0  false
+[1]
+    """, {
+      List a = [1]
+      int b = 0
+      assert a.get(b) == null
+    }
+  }
+
+  def "MethodCallExpression invoking static method"() {
+    expect:
+    // unlike in dynamic code, the target class of a static method call is not
+    // rendered, because statically compiled code never evaluates the receiver
+    // expression of a static method call, so its value is never recorded
+    isRendered """
+Math.max(a,b) == c
+|    |   | |  |  |
+|    2   1 2  |  0
+|             false
+class java.lang.Math
+    """, {
+      int a = 1
+      int b = 2
+      int c = 0
+      assert Math.max(a,b) == c
+    }
+  }
+
+  def "MethodCallExpression with spread-dot operator"() {
+    expect:
+    isRendered """
+["1", "22"]*.size() == null
+             |      |
+             [1, 2] false
+    """, {
+      assert ["1", "22"]*.size() == null
+    }
+  }
+
+  def "MethodCallExpression with safe operator"() {
+    expect:
+    isRendered """
+a?.length()
+|  |
+|  null
+null
+    """, {
+      String a = null
+      assert a?.length()
+    }
+  }
+
+  def "top-level MethodCallExpression"() {
+    expect:
+    isRendered """
+a.contains(b)
+| |        |
+| false    2
+[0]
+    """, {
+      List a = [0]
+      int b = 2
+      assert a.contains(b)
+    }
+  }
+
+  def "chained MethodCallExpressions"() {
+    expect:
+    isRendered """
+a.first().toUpperCase() == "B"
+| |       |             |
+| a       A             false
+[a]                     1 difference (0% similarity)
+                        (A)
+                        (B)
+    """, {
+      List a = ["a"]
+      assert a.first().toUpperCase() == "B"
+    }
+  }
+
+  def "TernaryExpression"() {
+    expect:
+    isRendered """
+a ? b : c
+|   |
+1   0
+    """, {
+      int a = 1
+      int b = 0
+      int c = 1
+      assert a ? b : c
+    }
+  }
+
+  def "BinaryExpression"() {
+    expect:
+    isRendered """
+a * b
+| | |
+0 0 1
+    """, {
+      int a = 0
+      int b = 1
+      assert a * b
+    }
+
+    isRendered """
+a[b]
+|||
+||0
+|false
+[false]
+    """, {
+      List a = [false]
+      int b = 0
+      assert a[b]
+    }
+  }
+
+  def "comparison operators"() {
+    expect:
+    isRendered """
+x > 43
+| |
+| false
+42
+    """, {
+      int x = 42
+      assert x > 43
+    }
+  }
+
+  def "BooleanExpression"() {
+    expect:
+    isRendered """
+a
+|
+null
+    """, {
+      String a = null
+      assert a
+    }
+  }
+
+  def "PropertyExpression"() {
+    expect:
+    isRendered """
+a.empty == true
+| |     |
+| false false
+[9]
+    """, {
+      List a = [9]
+      assert a.empty == true
+    }
+  }
+
+  def "instanceof expression"() {
+    expect:
+    isRendered """
+x instanceof Integer
+| |          |
+| false      class java.lang.Integer
+foo (java.lang.String)
+    """, {
+      Object x = "foo"
+      assert x instanceof Integer
+    }
+  }
+
+  def "VariableExpression"() {
+    expect:
+    isRendered """
+x
+|
+0
+    """, {
+      Integer x = 0
+      assert x
+    }
+  }
+
+  def "GStringExpression"() {
+    expect:
+    isRendered '''
+"$a and ${b + c}" == null
+  |       | | |   |
+  1       2 5 3   false
+    ''', {
+      int a = 1
+      int b = 2
+      int c = 3
+      assert "$a and ${b + c}" == null
+    }
+  }
+
+  def "condition in with block"() {
+    expect:
+    isRendered """
+size() == 3
+|      |
+2      false
+    """, {
+      List list = ["a", "b"]
+      with(list) {
+        assert size() == 3
+      }
+    }
+  }
+}
diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/mock/JavaStubs.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/JavaStubs.groovy
index d1ae7186c3..7fb9155d70 100644
--- a/spock-specs/src/test/groovy/org/spockframework/smoke/mock/JavaStubs.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/JavaStubs.groovy
@@ -14,9 +14,11 @@
 
 package org.spockframework.smoke.mock
 
+import org.spockframework.runtime.GroovyRuntimeUtil
 import org.spockframework.runtime.InvalidSpecException
 import org.spockframework.mock.CannotCreateMockException
 import spock.lang.Issue
+import spock.lang.PendingFeatureIf
 import spock.lang.Specification
 import spock.lang.FailsWith
 import spock.mock.MockMakers
@@ -65,7 +67,8 @@ class JavaStubs extends Specification {
   }
 
   @Issue("https://github.com/spockframework/spock/issues/1076")
-  def "can stub property access for implicit getProperty() call"() {
+  @PendingFeatureIf({ GroovyRuntimeUtil.MAJOR_VERSION < 4 })
+  def "can stub property access for implicit getProperty() call in assertion"() {
     given:
     person = Stub(Person)
 
@@ -78,6 +81,25 @@ class JavaStubs extends Specification {
     person.getProperty("age") == 25
   }
 
+  @Issue("https://github.com/spockframework/spock/issues/1076")
+  @PendingFeatureIf({ GroovyRuntimeUtil.MAJOR_VERSION < 4 })
+  def "can stub property access for implicit getProperty() call"() {
+    given:
+    person = Stub(Person)
+
+    and:
+    person.getName() >> "fred"
+    person.age >> 25
+
+    when:
+    def name = person.getProperty("name")
+    def age = person.getProperty("age")
+
+    then:
+    name == "fred"
+    age == 25
+  }
+
   def "like to be stubbed at creation time"() {
     given:
     person = Stub(IPerson) {
diff --git a/spock-specs/src/test/groovy/org/spockframework/verifyall/VerifyAllSpecification.groovy b/spock-specs/src/test/groovy/org/spockframework/verifyall/VerifyAllSpecification.groovy
index b2d52426e4..46a3ccfbdd 100644
--- a/spock-specs/src/test/groovy/org/spockframework/verifyall/VerifyAllSpecification.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/verifyall/VerifyAllSpecification.groovy
@@ -5,8 +5,9 @@ import org.spockframework.runtime.*
 import spock.lang.FailsWith
 import spock.lang.Issue
 
-import groovy.transform.*
+import groovy.transform.CompileStatic
 import org.opentest4j.MultipleFailuresError
+import spock.lang.SpockTypeChecked
 
 class VerifyAllSpecification extends EmbeddedSpecification {
 
@@ -218,7 +219,7 @@ class VerifyAllSpecification extends EmbeddedSpecification {
     }
   }
 
-  @TypeChecked
+  @SpockTypeChecked
   def "verifyAll with target"() {
     given:
     Person p = new Person()
@@ -230,7 +231,7 @@ class VerifyAllSpecification extends EmbeddedSpecification {
     }
   }
 
-  @TypeChecked
+  @SpockTypeChecked
   def "verifyAll with target and class"() {
     given:
     Object p = new Person()
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-[0].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-[0].groovy
new file mode 100644
index 0000000000..85d032065e
--- /dev/null
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-[0].groovy
@@ -0,0 +1,71 @@
+@org.spockframework.runtime.model.SpecMetadata(filename = 'script.groovy', line = 1)
+public class apackage.ASpec extends spock.lang.Specification {
+
+    @spock.lang.SpockCompileDynamic
+    @org.spockframework.runtime.model.FeatureMetadata(name = 'type-checked where block', ordinal = 0, line = 2, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.FILTER, texts = [])], parameterNames = ['bar', 'baz', 'bam'])
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    public void $spock_feature_0_0(java.lang.Object bar, java.lang.Object baz, java.lang.Object bam) {
+        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)
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 5, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 5, 5, null, $spock_condition_throwable)}
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
+        this.getSpecificationContext().getMockController().leaveScope()
+    }
+
+    @org.spockframework.runtime.model.DataProviderMetadata(line = 10, dataVariables = ['baz'])
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    public java.lang.Object $spock_feature_0_0prov0(java.lang.Object foo) {
+        return [[].does_not_exist()]
+    }
+
+    @org.spockframework.runtime.model.DataProviderMetadata(line = 12, dataVariables = ['bam'])
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    public java.lang.Object $spock_feature_0_0prov1(java.lang.Object foo) {
+        return [[].does_not_exist()]
+    }
+
+    @org.spockframework.runtime.model.DataProcessorMetadata(dataVariables = ['bar', 'baz', 'bam'])
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0, java.lang.Object $spock_p1, java.lang.Object foo) {
+        java.lang.Object bar = (([].does_not_exist()) as java.lang.Object)
+        java.lang.Object baz = ($spock_p0 as java.lang.Object)
+        java.lang.Object bam = ($spock_p1 as java.lang.Object)
+        return new java.lang.Object[]{bar, baz, bam}
+    }
+
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    public java.lang.Object[] $spock_feature_0_0wherevars() {
+        java.lang.Object[] $spock_whereVariableValues = new java.lang.Object[1]{}
+        try {
+            java.lang.Object foo = [].does_not_exist()
+            $spock_whereVariableValues[0] = foo
+            return $spock_whereVariableValues
+        }
+        catch (java.lang.Throwable $spock_tmp_throwable) {
+            org.spockframework.runtime.SpockRuntime.closeWhereBlockVariablesAfterFailure($spock_whereVariableValues, $spock_tmp_throwable)
+            throw $spock_tmp_throwable
+        }
+    }
+
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    public static org.spockframework.runtime.model.DataVariableMultiplication[] $spock_feature_0_0prods() {
+        return new org.spockframework.runtime.model.DataVariableMultiplication[]{new org.spockframework.runtime.model.DataVariableMultiplication(new java.lang.String[]{'baz', 'bam'}, new org.spockframework.runtime.model.DataVariableMultiplicationFactor(new java.lang.String[]{'baz'}), new org.spockframework.runtime.model.DataVariableMultiplicationFactor(new java.lang.String[]{'bam'}))}
+    }
+
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    public void $spock_feature_0_0filter(java.lang.Object bar, java.lang.Object baz, java.lang.Object bam, java.lang.Object foo) {
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[[].does_not_exist()]', 14, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), []).does_not_exist())]))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[[].does_not_exist()]', 14, 5, null, $spock_condition_throwable)}
+    }
+
+}
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-[1].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-[1].groovy
new file mode 100644
index 0000000000..2a5295dcbc
--- /dev/null
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-[1].groovy
@@ -0,0 +1,71 @@
+@org.spockframework.runtime.model.SpecMetadata(filename = 'script.groovy', line = 1)
+public class apackage.ASpec extends spock.lang.Specification {
+
+    @spock.lang.SpockTypeChecked
+    @groovy.transform.TypeChecked
+    @org.spockframework.runtime.model.FeatureMetadata(name = 'type-checked where block', ordinal = 0, line = 2, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.FILTER, texts = [])], parameterNames = ['bar', 'baz', 'bam'])
+    public void $spock_feature_0_0(java.lang.Object bar, java.lang.Object baz, java.lang.Object bam) {
+        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)
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 5, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 5, 5, null, $spock_condition_throwable)}
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
+        this.getSpecificationContext().getMockController().leaveScope()
+    }
+
+    @org.spockframework.runtime.model.DataProviderMetadata(line = 10, dataVariables = ['baz'])
+    @groovy.transform.TypeChecked
+    public java.lang.Object $spock_feature_0_0prov0(java.lang.Object foo) {
+        return [[].does_not_exist()]
+    }
+
+    @org.spockframework.runtime.model.DataProviderMetadata(line = 12, dataVariables = ['bam'])
+    @groovy.transform.TypeChecked
+    public java.lang.Object $spock_feature_0_0prov1(java.lang.Object foo) {
+        return [[].does_not_exist()]
+    }
+
+    @org.spockframework.runtime.model.DataProcessorMetadata(dataVariables = ['bar', 'baz', 'bam'])
+    @groovy.transform.TypeChecked
+    public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0, java.lang.Object $spock_p1, java.lang.Object foo) {
+        java.lang.Object bar = (([].does_not_exist()) as java.lang.Object)
+        java.lang.Object baz = ($spock_p0 as java.lang.Object)
+        java.lang.Object bam = ($spock_p1 as java.lang.Object)
+        return new java.lang.Object[]{bar, baz, bam}
+    }
+
+    @groovy.transform.TypeChecked
+    public java.lang.Object[] $spock_feature_0_0wherevars() {
+        java.lang.Object[] $spock_whereVariableValues = new java.lang.Object[1]{}
+        try {
+            java.lang.Object foo = [].does_not_exist()
+            $spock_whereVariableValues[0] = foo
+            return $spock_whereVariableValues
+        }
+        catch (java.lang.Throwable $spock_tmp_throwable) {
+            org.spockframework.runtime.SpockRuntime.closeWhereBlockVariablesAfterFailure($spock_whereVariableValues, $spock_tmp_throwable)
+            throw $spock_tmp_throwable
+        }
+    }
+
+    @groovy.transform.TypeChecked
+    public static org.spockframework.runtime.model.DataVariableMultiplication[] $spock_feature_0_0prods() {
+        return new org.spockframework.runtime.model.DataVariableMultiplication[]{new org.spockframework.runtime.model.DataVariableMultiplication(new java.lang.String[]{'baz', 'bam'}, new org.spockframework.runtime.model.DataVariableMultiplicationFactor(new java.lang.String[]{'baz'}), new org.spockframework.runtime.model.DataVariableMultiplicationFactor(new java.lang.String[]{'bam'}))}
+    }
+
+    @groovy.transform.TypeChecked
+    public void $spock_feature_0_0filter(java.lang.Object bar, java.lang.Object baz, java.lang.Object bam, java.lang.Object foo) {
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[[].does_not_exist()]', 14, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), []).does_not_exist())]))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[[].does_not_exist()]', 14, 5, null, $spock_condition_throwable)}
+    }
+
+}
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-[2].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-[2].groovy
new file mode 100644
index 0000000000..95ac391352
--- /dev/null
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-[2].groovy
@@ -0,0 +1,71 @@
+@org.spockframework.runtime.model.SpecMetadata(filename = 'script.groovy', line = 1)
+public class apackage.ASpec extends spock.lang.Specification {
+
+    @spock.lang.SpockCompileStatic
+    @groovy.transform.CompileStatic
+    @org.spockframework.runtime.model.FeatureMetadata(name = 'type-checked where block', ordinal = 0, line = 2, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.FILTER, texts = [])], parameterNames = ['bar', 'baz', 'bam'])
+    public void $spock_feature_0_0(java.lang.Object bar, java.lang.Object baz, java.lang.Object bam) {
+        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)
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 5, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 5, 5, null, $spock_condition_throwable)}
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
+        this.getSpecificationContext().getMockController().leaveScope()
+    }
+
+    @org.spockframework.runtime.model.DataProviderMetadata(line = 10, dataVariables = ['baz'])
+    @groovy.transform.CompileStatic
+    public java.lang.Object $spock_feature_0_0prov0(java.lang.Object foo) {
+        return [[].does_not_exist()]
+    }
+
+    @org.spockframework.runtime.model.DataProviderMetadata(line = 12, dataVariables = ['bam'])
+    @groovy.transform.CompileStatic
+    public java.lang.Object $spock_feature_0_0prov1(java.lang.Object foo) {
+        return [[].does_not_exist()]
+    }
+
+    @org.spockframework.runtime.model.DataProcessorMetadata(dataVariables = ['bar', 'baz', 'bam'])
+    @groovy.transform.CompileStatic
+    public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0, java.lang.Object $spock_p1, java.lang.Object foo) {
+        java.lang.Object bar = (([].does_not_exist()) as java.lang.Object)
+        java.lang.Object baz = ($spock_p0 as java.lang.Object)
+        java.lang.Object bam = ($spock_p1 as java.lang.Object)
+        return new java.lang.Object[]{bar, baz, bam}
+    }
+
+    @groovy.transform.CompileStatic
+    public java.lang.Object[] $spock_feature_0_0wherevars() {
+        java.lang.Object[] $spock_whereVariableValues = new java.lang.Object[1]{}
+        try {
+            java.lang.Object foo = [].does_not_exist()
+            $spock_whereVariableValues[0] = foo
+            return $spock_whereVariableValues
+        }
+        catch (java.lang.Throwable $spock_tmp_throwable) {
+            org.spockframework.runtime.SpockRuntime.closeWhereBlockVariablesAfterFailure($spock_whereVariableValues, $spock_tmp_throwable)
+            throw $spock_tmp_throwable
+        }
+    }
+
+    @groovy.transform.CompileStatic
+    public static org.spockframework.runtime.model.DataVariableMultiplication[] $spock_feature_0_0prods() {
+        return new org.spockframework.runtime.model.DataVariableMultiplication[]{new org.spockframework.runtime.model.DataVariableMultiplication(new java.lang.String[]{'baz', 'bam'}, new org.spockframework.runtime.model.DataVariableMultiplicationFactor(new java.lang.String[]{'baz'}), new org.spockframework.runtime.model.DataVariableMultiplicationFactor(new java.lang.String[]{'bam'}))}
+    }
+
+    @groovy.transform.CompileStatic
+    public void $spock_feature_0_0filter(java.lang.Object bar, java.lang.Object baz, java.lang.Object bam, java.lang.Object foo) {
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[[].does_not_exist()]', 14, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), []).does_not_exist())]))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[[].does_not_exist()]', 14, 5, null, $spock_condition_throwable)}
+    }
+
+}
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-groovy25-[0].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-groovy25-[0].groovy
new file mode 100644
index 0000000000..d838f47109
--- /dev/null
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-groovy25-[0].groovy
@@ -0,0 +1,71 @@
+@org.spockframework.runtime.model.SpecMetadata(filename = 'script.groovy', line = 1)
+public class apackage.ASpec extends spock.lang.Specification {
+
+    @spock.lang.SpockCompileDynamic
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    @org.spockframework.runtime.model.FeatureMetadata(name = 'type-checked where block', ordinal = 0, line = 2, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.FILTER, texts = [])], parameterNames = ['bar', 'baz', 'bam'])
+    public void $spock_feature_0_0(java.lang.Object bar, java.lang.Object baz, java.lang.Object bam) {
+        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)
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 5, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 5, 5, null, $spock_condition_throwable)}
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
+        this.getSpecificationContext().getMockController().leaveScope()
+    }
+
+    @org.spockframework.runtime.model.DataProviderMetadata(line = 10, dataVariables = ['baz'])
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    public java.lang.Object $spock_feature_0_0prov0(java.lang.Object foo) {
+        return [[].does_not_exist()]
+    }
+
+    @org.spockframework.runtime.model.DataProviderMetadata(line = 12, dataVariables = ['bam'])
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    public java.lang.Object $spock_feature_0_0prov1(java.lang.Object foo) {
+        return [[].does_not_exist()]
+    }
+
+    @org.spockframework.runtime.model.DataProcessorMetadata(dataVariables = ['bar', 'baz', 'bam'])
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0, java.lang.Object $spock_p1, java.lang.Object foo) {
+        java.lang.Object bar = (([].does_not_exist()) as java.lang.Object)
+        java.lang.Object baz = ($spock_p0 as java.lang.Object)
+        java.lang.Object bam = ($spock_p1 as java.lang.Object)
+        return new java.lang.Object[]{bar, baz, bam}
+    }
+
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    public java.lang.Object[] $spock_feature_0_0wherevars() {
+        java.lang.Object[] $spock_whereVariableValues = new java.lang.Object[1]{}
+        try {
+            java.lang.Object foo = [].does_not_exist()
+            $spock_whereVariableValues[0] = foo
+            return $spock_whereVariableValues
+        }
+        catch (java.lang.Throwable $spock_tmp_throwable) {
+            org.spockframework.runtime.SpockRuntime.closeWhereBlockVariablesAfterFailure($spock_whereVariableValues, $spock_tmp_throwable)
+            throw $spock_tmp_throwable
+        }
+    }
+
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    public static org.spockframework.runtime.model.DataVariableMultiplication[] $spock_feature_0_0prods() {
+        return new org.spockframework.runtime.model.DataVariableMultiplication[]{new org.spockframework.runtime.model.DataVariableMultiplication(new java.lang.String[]{'baz', 'bam'}, new org.spockframework.runtime.model.DataVariableMultiplicationFactor(new java.lang.String[]{'baz'}), new org.spockframework.runtime.model.DataVariableMultiplicationFactor(new java.lang.String[]{'bam'}))}
+    }
+
+    @groovy.transform.CompileStatic(value = groovy.transform.TypeCheckingMode.SKIP)
+    public void $spock_feature_0_0filter(java.lang.Object bar, java.lang.Object baz, java.lang.Object bam, java.lang.Object foo) {
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[[].does_not_exist()]', 14, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), []).does_not_exist())]))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[[].does_not_exist()]', 14, 5, null, $spock_condition_throwable)}
+    }
+
+}
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-groovy25-[1].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-groovy25-[1].groovy
new file mode 100644
index 0000000000..2a5295dcbc
--- /dev/null
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-groovy25-[1].groovy
@@ -0,0 +1,71 @@
+@org.spockframework.runtime.model.SpecMetadata(filename = 'script.groovy', line = 1)
+public class apackage.ASpec extends spock.lang.Specification {
+
+    @spock.lang.SpockTypeChecked
+    @groovy.transform.TypeChecked
+    @org.spockframework.runtime.model.FeatureMetadata(name = 'type-checked where block', ordinal = 0, line = 2, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.FILTER, texts = [])], parameterNames = ['bar', 'baz', 'bam'])
+    public void $spock_feature_0_0(java.lang.Object bar, java.lang.Object baz, java.lang.Object bam) {
+        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)
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 5, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 5, 5, null, $spock_condition_throwable)}
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
+        this.getSpecificationContext().getMockController().leaveScope()
+    }
+
+    @org.spockframework.runtime.model.DataProviderMetadata(line = 10, dataVariables = ['baz'])
+    @groovy.transform.TypeChecked
+    public java.lang.Object $spock_feature_0_0prov0(java.lang.Object foo) {
+        return [[].does_not_exist()]
+    }
+
+    @org.spockframework.runtime.model.DataProviderMetadata(line = 12, dataVariables = ['bam'])
+    @groovy.transform.TypeChecked
+    public java.lang.Object $spock_feature_0_0prov1(java.lang.Object foo) {
+        return [[].does_not_exist()]
+    }
+
+    @org.spockframework.runtime.model.DataProcessorMetadata(dataVariables = ['bar', 'baz', 'bam'])
+    @groovy.transform.TypeChecked
+    public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0, java.lang.Object $spock_p1, java.lang.Object foo) {
+        java.lang.Object bar = (([].does_not_exist()) as java.lang.Object)
+        java.lang.Object baz = ($spock_p0 as java.lang.Object)
+        java.lang.Object bam = ($spock_p1 as java.lang.Object)
+        return new java.lang.Object[]{bar, baz, bam}
+    }
+
+    @groovy.transform.TypeChecked
+    public java.lang.Object[] $spock_feature_0_0wherevars() {
+        java.lang.Object[] $spock_whereVariableValues = new java.lang.Object[1]{}
+        try {
+            java.lang.Object foo = [].does_not_exist()
+            $spock_whereVariableValues[0] = foo
+            return $spock_whereVariableValues
+        }
+        catch (java.lang.Throwable $spock_tmp_throwable) {
+            org.spockframework.runtime.SpockRuntime.closeWhereBlockVariablesAfterFailure($spock_whereVariableValues, $spock_tmp_throwable)
+            throw $spock_tmp_throwable
+        }
+    }
+
+    @groovy.transform.TypeChecked
+    public static org.spockframework.runtime.model.DataVariableMultiplication[] $spock_feature_0_0prods() {
+        return new org.spockframework.runtime.model.DataVariableMultiplication[]{new org.spockframework.runtime.model.DataVariableMultiplication(new java.lang.String[]{'baz', 'bam'}, new org.spockframework.runtime.model.DataVariableMultiplicationFactor(new java.lang.String[]{'baz'}), new org.spockframework.runtime.model.DataVariableMultiplicationFactor(new java.lang.String[]{'bam'}))}
+    }
+
+    @groovy.transform.TypeChecked
+    public void $spock_feature_0_0filter(java.lang.Object bar, java.lang.Object baz, java.lang.Object bam, java.lang.Object foo) {
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[[].does_not_exist()]', 14, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), []).does_not_exist())]))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[[].does_not_exist()]', 14, 5, null, $spock_condition_throwable)}
+    }
+
+}
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-groovy25-[2].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-groovy25-[2].groovy
new file mode 100644
index 0000000000..95ac391352
--- /dev/null
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/STC_annotations_are_transplanted_to_generated_methods_with___annotation_simpleName-groovy25-[2].groovy
@@ -0,0 +1,71 @@
+@org.spockframework.runtime.model.SpecMetadata(filename = 'script.groovy', line = 1)
+public class apackage.ASpec extends spock.lang.Specification {
+
+    @spock.lang.SpockCompileStatic
+    @groovy.transform.CompileStatic
+    @org.spockframework.runtime.model.FeatureMetadata(name = 'type-checked where block', ordinal = 0, line = 2, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.FILTER, texts = [])], parameterNames = ['bar', 'baz', 'bam'])
+    public void $spock_feature_0_0(java.lang.Object bar, java.lang.Object baz, java.lang.Object bam) {
+        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)
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 5, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 5, 5, null, $spock_condition_throwable)}
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
+        this.getSpecificationContext().getMockController().leaveScope()
+    }
+
+    @org.spockframework.runtime.model.DataProviderMetadata(line = 10, dataVariables = ['baz'])
+    @groovy.transform.CompileStatic
+    public java.lang.Object $spock_feature_0_0prov0(java.lang.Object foo) {
+        return [[].does_not_exist()]
+    }
+
+    @org.spockframework.runtime.model.DataProviderMetadata(line = 12, dataVariables = ['bam'])
+    @groovy.transform.CompileStatic
+    public java.lang.Object $spock_feature_0_0prov1(java.lang.Object foo) {
+        return [[].does_not_exist()]
+    }
+
+    @org.spockframework.runtime.model.DataProcessorMetadata(dataVariables = ['bar', 'baz', 'bam'])
+    @groovy.transform.CompileStatic
+    public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0, java.lang.Object $spock_p1, java.lang.Object foo) {
+        java.lang.Object bar = (([].does_not_exist()) as java.lang.Object)
+        java.lang.Object baz = ($spock_p0 as java.lang.Object)
+        java.lang.Object bam = ($spock_p1 as java.lang.Object)
+        return new java.lang.Object[]{bar, baz, bam}
+    }
+
+    @groovy.transform.CompileStatic
+    public java.lang.Object[] $spock_feature_0_0wherevars() {
+        java.lang.Object[] $spock_whereVariableValues = new java.lang.Object[1]{}
+        try {
+            java.lang.Object foo = [].does_not_exist()
+            $spock_whereVariableValues[0] = foo
+            return $spock_whereVariableValues
+        }
+        catch (java.lang.Throwable $spock_tmp_throwable) {
+            org.spockframework.runtime.SpockRuntime.closeWhereBlockVariablesAfterFailure($spock_whereVariableValues, $spock_tmp_throwable)
+            throw $spock_tmp_throwable
+        }
+    }
+
+    @groovy.transform.CompileStatic
+    public static org.spockframework.runtime.model.DataVariableMultiplication[] $spock_feature_0_0prods() {
+        return new org.spockframework.runtime.model.DataVariableMultiplication[]{new org.spockframework.runtime.model.DataVariableMultiplication(new java.lang.String[]{'baz', 'bam'}, new org.spockframework.runtime.model.DataVariableMultiplicationFactor(new java.lang.String[]{'baz'}), new org.spockframework.runtime.model.DataVariableMultiplicationFactor(new java.lang.String[]{'bam'}))}
+    }
+
+    @groovy.transform.CompileStatic
+    public void $spock_feature_0_0filter(java.lang.Object bar, java.lang.Object baz, java.lang.Object bam, java.lang.Object foo) {
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[[].does_not_exist()]', 14, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), []).does_not_exist())]))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[[].does_not_exist()]', 14, 5, null, $spock_condition_throwable)}
+    }
+
+}
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
index f89c4c10a6..eb66ad9c56 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
@@ -18,7 +18,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
         org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 1)
         try {
-            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
+            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, 'println', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
         }
         catch (java.lang.Throwable $spock_condition_throwable) {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy
index 5cf028c956..f85d6f97fd 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy
@@ -18,7 +18,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
         org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 1)
         try {
-            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
+            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, 'println', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
         }
         catch (java.lang.Throwable $spock_condition_throwable) {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
index 5cf028c956..f85d6f97fd 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
@@ -18,7 +18,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
         org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 1)
         try {
-            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
+            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, 'println', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
         }
         catch (java.lang.Throwable $spock_condition_throwable) {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
index 559426607e..77824f2171 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
@@ -13,7 +13,7 @@ public void $spock_feature_0_0() {
     org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
     org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 1)
     try {
-        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsAsSet'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
+        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, 'matchCollectionsAsSet', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
     }
     catch (java.lang.Throwable $spock_condition_throwable) {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ [1]', 4, 9, null, $spock_condition_throwable)}
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
index 1c5f107dd4..33398d5745 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
@@ -13,7 +13,7 @@ public void $spock_feature_0_0() {
     org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
     org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 1)
     try {
-        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsInAnyOrder'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
+        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, 'matchCollectionsInAnyOrder', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
     }
     catch (java.lang.Throwable $spock_condition_throwable) {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ [1]', 4, 9, null, $spock_condition_throwable)}
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_explicit_condition.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_explicit_condition.groovy
index e40f91ed27..7054aafe90 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_explicit_condition.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_explicit_condition.groovy
@@ -4,7 +4,7 @@ public void $spock_feature_0_0() {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 0)
     try {
-        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'null.with {\n  false\n}', 2, 8, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), null), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 'with'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), {
+        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'null.with {\n  false\n}', 2, 8, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), null), 'with', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), {
             false
         })}, $spock_valueRecorder.realizeNas(5, false), true, 4)
     }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_implicit_condition.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_implicit_condition.groovy
index ec43525cda..65d59bbcbe 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_implicit_condition.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_implicit_condition.groovy
@@ -4,7 +4,7 @@ public void $spock_feature_0_0() {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 0)
     try {
-        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'null.with {\n  false\n}', 2, 1, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), null), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 'with'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), {
+        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'null.with {\n  false\n}', 2, 1, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), null), 'with', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), {
             false
         })}, $spock_valueRecorder.realizeNas(5, false), false, 4)
     }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_private_methods.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_private_methods.groovy
index c09609c5cc..e64b62bad4 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_private_methods.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_private_methods.groovy
@@ -14,39 +14,51 @@ private void isPositive(int a) {
         catch (java.lang.Throwable $spock_condition_throwable) {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a > 0', 17, 5, null, $spock_condition_throwable)}
         try {
-            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 18, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), {
+            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 18, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), {
                 it
             })}, $spock_valueRecorder.realizeNas(7, false), false, 6)
         }
         catch (java.lang.Throwable $spock_condition_throwable) {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[true, false].any { it }', 18, 5, null, $spock_condition_throwable)}
+        if (false) {
+            ((java.util.List) null).&'any'
+        }
         if (true) {
             try {
-                org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 20, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), {
+                org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 20, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), {
                     it
                 })}, $spock_valueRecorder.realizeNas(7, false), false, 6)
             }
             catch (java.lang.Throwable $spock_condition_throwable) {
                 org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[true, false].any { it }', 20, 9, null, $spock_condition_throwable)}
+            if (false) {
+                ((java.util.List) null).&'any'
+            }
         }
         try {
             this.with({
                 org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder()
                 try {
-                    org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 23, 9, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
+                    org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 23, 9, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
                         it
                     })}, $spock_valueRecorder1.realizeNas(7, false), false, 6)
                 }
                 catch (java.lang.Throwable $spock_condition_throwable) {
                     org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, '[true, false].any { it }', 23, 9, null, $spock_condition_throwable)}
+                if (false) {
+                    ((java.util.List) null).&'any'
+                }
                 if (true) {
                     try {
-                        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 25, 13, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
+                        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 25, 13, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
                             it
                         })}, $spock_valueRecorder1.realizeNas(7, false), false, 6)
                     }
                     catch (java.lang.Throwable $spock_condition_throwable) {
                         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, '[true, false].any { it }', 25, 13, null, $spock_condition_throwable)}
+                    if (false) {
+                        ((java.util.List) null).&'any'
+                    }
                 }
             })
         }
@@ -63,12 +75,15 @@ public void $spock_feature_0_0() {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 0)
     try {
-        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 3, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), {
+        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 3, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), {
             it
         })}, $spock_valueRecorder.realizeNas(7, false), false, 6)
     }
     catch (java.lang.Throwable $spock_condition_throwable) {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[true, false].any { it }', 3, 5, null, $spock_condition_throwable)}
+    if (false) {
+        ((java.util.List) null).&'any'
+    }
     if (true) {
         [true, false].any({
             it
@@ -77,20 +92,26 @@ public void $spock_feature_0_0() {
     this.with({
         org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder()
         try {
-            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 8, 9, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
+            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 8, 9, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
                 it
             })}, $spock_valueRecorder1.realizeNas(7, false), false, 6)
         }
         catch (java.lang.Throwable $spock_condition_throwable) {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, '[true, false].any { it }', 8, 9, null, $spock_condition_throwable)}
+        if (false) {
+            ((java.util.List) null).&'any'
+        }
         if (true) {
             try {
-                org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 10, 13, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
+                org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 10, 13, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
                     it
                 })}, $spock_valueRecorder1.realizeNas(7, false), false, 6)
             }
             catch (java.lang.Throwable $spock_condition_throwable) {
                 org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, '[true, false].any { it }', 10, 13, null, $spock_condition_throwable)}
+            if (false) {
+                ((java.util.List) null).&'any'
+            }
         }
     })
     org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_private_methods.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_private_methods.groovy
index 8230fa4b63..ece815ec4e 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_private_methods.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_private_methods.groovy
@@ -13,39 +13,51 @@ private void isPositive(int a) {
     catch (java.lang.Throwable $spock_condition_throwable) {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a > 0', 17, 5, null, $spock_condition_throwable)}
     try {
-        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 18, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), {
+        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 18, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), {
             it
         })}, $spock_valueRecorder.realizeNas(7, false), false, 6)
     }
     catch (java.lang.Throwable $spock_condition_throwable) {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[true, false].any { it }', 18, 5, null, $spock_condition_throwable)}
+    if (false) {
+        ((java.util.List) null).&'any'
+    }
     if (true) {
         try {
-            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 20, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), {
+            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 20, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), {
                 it
             })}, $spock_valueRecorder.realizeNas(7, false), false, 6)
         }
         catch (java.lang.Throwable $spock_condition_throwable) {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[true, false].any { it }', 20, 9, null, $spock_condition_throwable)}
+        if (false) {
+            ((java.util.List) null).&'any'
+        }
     }
     try {
         this.with({
             org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder()
             try {
-                org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 23, 9, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
+                org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 23, 9, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
                     it
                 })}, $spock_valueRecorder1.realizeNas(7, false), false, 6)
             }
             catch (java.lang.Throwable $spock_condition_throwable) {
                 org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, '[true, false].any { it }', 23, 9, null, $spock_condition_throwable)}
+            if (false) {
+                ((java.util.List) null).&'any'
+            }
             if (true) {
                 try {
-                    org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 25, 13, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
+                    org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 25, 13, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
                         it
                     })}, $spock_valueRecorder1.realizeNas(7, false), false, 6)
                 }
                 catch (java.lang.Throwable $spock_condition_throwable) {
                     org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, '[true, false].any { it }', 25, 13, null, $spock_condition_throwable)}
+                if (false) {
+                    ((java.util.List) null).&'any'
+                }
             }
         })
     }
@@ -59,12 +71,15 @@ public void $spock_feature_0_0() {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 0)
     try {
-        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 3, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), {
+        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 3, 5, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), {
             it
         })}, $spock_valueRecorder.realizeNas(7, false), false, 6)
     }
     catch (java.lang.Throwable $spock_condition_throwable) {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[true, false].any { it }', 3, 5, null, $spock_condition_throwable)}
+    if (false) {
+        ((java.util.List) null).&'any'
+    }
     if (true) {
         [true, false].any({
             it
@@ -73,20 +88,26 @@ public void $spock_feature_0_0() {
     this.with({
         org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder()
         try {
-            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 8, 9, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
+            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 8, 9, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
                 it
             })}, $spock_valueRecorder1.realizeNas(7, false), false, 6)
         }
         catch (java.lang.Throwable $spock_condition_throwable) {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, '[true, false].any { it }', 8, 9, null, $spock_condition_throwable)}
+        if (false) {
+            ((java.util.List) null).&'any'
+        }
         if (true) {
             try {
-                org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 10, 13, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
+                org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder1.reset(), '[true, false].any { it }', 10, 13, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(2), [$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true), $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(1), false)]), 'any', new java.lang.Object[]{$spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(4), {
                     it
                 })}, $spock_valueRecorder1.realizeNas(7, false), false, 6)
             }
             catch (java.lang.Throwable $spock_condition_throwable) {
                 org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, '[true, false].any { it }', 10, 13, null, $spock_condition_throwable)}
+            if (false) {
+                ((java.util.List) null).&'any'
+            }
         }
     })
     org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
index 2b63ee3b75..b29a2d0ca4 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
@@ -10,6 +10,9 @@ public void $spock_feature_0_0() {
     java.util.List list = org.spockframework.runtime.SpecInternals.MockImpl(this, 'list', java.util.List)
     this.getSpecificationContext().getMockController().enterScope()
     this.getSpecificationContext().getMockController().addInteraction(new org.spockframework.mock.runtime.InteractionBuilder(8, 5, '1 * list.add(1)').setFixedCount(1).addEqualTarget(list).addEqualMethodName('add').setArgListKind(true, false).addEqualArg(1).build())
+    if (false) {
+        ((java.util.List) null).&'add'
+    }
     org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
     org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 1)
     list.add(1)