You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2021/05/16 11:36:42 UTC

[groovy] branch master updated: GROOVY-9632: Java 8 Type Param Annotation Not Generated in Byte Code

This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new a258e2f  GROOVY-9632: Java 8 Type Param Annotation Not Generated in Byte Code
a258e2f is described below

commit a258e2fd8db9c8b82998db8f4c980cc8332e4756
Author: Paul King <pa...@asert.com.au>
AuthorDate: Mon May 10 22:07:51 2021 +1000

    GROOVY-9632: Java 8 Type Param Annotation Not Generated in Byte Code
---
 src/antlr/GroovyParser.g4                          |   2 +-
 .../apache/groovy/ast/tools/ClassNodeUtils.java    |   4 +-
 .../apache/groovy/ast/tools/MethodNodeUtils.java   |   9 +-
 .../apache/groovy/parser/antlr4/AstBuilder.java    |  49 ++--
 .../org/codehaus/groovy/antlr/PrimitiveHelper.java |  26 ++-
 .../java/org/codehaus/groovy/ast/ClassHelper.java  |  51 +++--
 .../java/org/codehaus/groovy/ast/ClassNode.java    |  45 +++-
 .../java/org/codehaus/groovy/ast/FieldNode.java    |   3 +-
 .../java/org/codehaus/groovy/ast/MethodNode.java   |   4 +-
 .../java/org/codehaus/groovy/ast/ModuleNode.java   |   3 +-
 .../java/org/codehaus/groovy/ast/Parameter.java    |   3 +-
 .../groovy/ast/expr/VariableExpression.java        |  26 ++-
 .../org/codehaus/groovy/ast/tools/BeanUtils.java   |   3 +-
 .../codehaus/groovy/ast/tools/GeneralUtils.java    |   8 +-
 .../codehaus/groovy/ast/tools/GenericsUtils.java   |   4 +-
 .../groovy/ast/tools/WideningCategories.java       |  43 ++--
 .../groovy/classgen/AsmClassGenerator.java         | 164 +++++++++++--
 .../groovy/classgen/ClassCompletionVerifier.java   |   8 +-
 .../codehaus/groovy/classgen/ExtendedVerifier.java | 139 +++++++++--
 .../groovy/classgen/InnerClassVisitorHelper.java   |   2 +-
 .../org/codehaus/groovy/classgen/Verifier.java     |  19 +-
 .../classgen/asm/BinaryExpressionHelper.java       |   2 +-
 .../classgen/asm/BinaryObjectExpressionHelper.java |   2 +-
 .../groovy/classgen/asm/BytecodeHelper.java        |  59 ++---
 .../groovy/classgen/asm/BytecodeVariable.java      |   3 +-
 .../groovy/classgen/asm/ClosureWriter.java         |   2 +-
 .../codehaus/groovy/classgen/asm/CompileStack.java |  13 +-
 .../groovy/classgen/asm/InvocationWriter.java      |   5 +-
 .../codehaus/groovy/classgen/asm/MopWriter.java    |   4 +-
 .../codehaus/groovy/classgen/asm/OperandStack.java |  99 ++++----
 .../classgen/asm/OptimizingStatementWriter.java    |   3 +-
 .../groovy/classgen/asm/StatementWriter.java       |   3 +-
 .../classgen/asm/indy/InvokeDynamicWriter.java     |   3 +-
 .../asm/sc/AbstractFunctionalInterfaceWriter.java  |   5 +-
 .../classgen/asm/sc/StaticInvocationWriter.java    |   5 +-
 ...icTypesBinaryExpressionMultiTypeDispatcher.java |  18 +-
 .../classgen/asm/sc/StaticTypesCallSiteWriter.java |   4 +-
 .../asm/sc/StaticTypesStatementWriter.java         |  45 ++--
 .../classgen/asm/sc/StaticTypesTypeChooser.java    |   5 +-
 .../asm/sc/StaticTypesUnaryExpressionHelper.java   |  47 ++--
 .../groovy/classgen/asm/util/TypeUtil.java         |  43 +++-
 .../codehaus/groovy/control/ResolveVisitor.java    |  59 ++++-
 .../groovy/runtime/ProxyGeneratorAdapter.java      |   2 +-
 .../groovy/tools/javac/JavaStubGenerator.java      |  35 +--
 .../transform/AutoCloneASTTransformation.java      |   4 +-
 .../transform/AutoImplementASTTransformation.java  |   3 +-
 .../transform/DelegateASTTransformation.java       |   8 +-
 .../ExternalizeMethodsASTTransformation.java       |  23 +-
 .../groovy/transform/LazyASTTransformation.java    |   3 +-
 .../transformers/BooleanExpressionTransformer.java |   3 +-
 .../MethodCallExpressionTransformer.java           |   6 +-
 .../codehaus/groovy/transform/stc/Receiver.java    |   2 +-
 .../transform/stc/StaticTypeCheckingSupport.java   |   3 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   |  54 +++--
 .../transform/trait/SuperCallTraitTransformer.java |   6 +-
 .../transform/trait/TraitASTTransformation.java    |   5 +-
 .../org/codehaus/groovy/ast/MethodNodeTest.groovy  |   2 +-
 .../groovy/classgen/asm/TypeAnnotationsTest.groovy | 253 +++++++++++++++++++++
 .../console/ui/ScriptToTreeNodeAdapterTest.groovy  |   2 +-
 .../ast/visitor/AnnotationProcessorVisitor.java    |   4 +-
 .../classgen/asm/ContractClosureWriter.java        |   3 +-
 .../groovy/contracts/generation/BaseGenerator.java |   3 +-
 .../generation/ClassInvariantGenerator.java        |   3 +-
 .../generation/PostconditionGenerator.java         |   3 +-
 64 files changed, 1089 insertions(+), 385 deletions(-)

diff --git a/src/antlr/GroovyParser.g4 b/src/antlr/GroovyParser.g4
index b7af1e6..f971d33 100644
--- a/src/antlr/GroovyParser.g4
+++ b/src/antlr/GroovyParser.g4
@@ -199,7 +199,7 @@ typeParameters
     ;
 
 typeParameter
-    :   className (EXTENDS nls typeBound)?
+    :   annotationsOpt className (EXTENDS nls typeBound)?
     ;
 
 typeBound
diff --git a/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java b/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
index 984c530..887ee8d 100644
--- a/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
+++ b/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
@@ -48,8 +48,8 @@ import java.util.function.Predicate;
 
 import static org.apache.groovy.ast.tools.AnnotatedNodeUtils.isGenerated;
 import static org.apache.groovy.ast.tools.AnnotatedNodeUtils.markAsGenerated;
-import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
 import static org.codehaus.groovy.runtime.ArrayTypeUtils.dimension;
 import static org.codehaus.groovy.runtime.ArrayTypeUtils.elementType;
 import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
@@ -291,7 +291,7 @@ public class ClassNodeUtils {
         }
         String propName = getPropNameForAccessor(methodName);
         PropertyNode pNode = getStaticProperty(cNode, propName);
-        return pNode != null && (methodName.startsWith("get") || boolean_TYPE.equals(pNode.getType()));
+        return pNode != null && (methodName.startsWith("get") || isPrimitiveBoolean(pNode.getType()));
     }
 
     /**
diff --git a/src/main/java/org/apache/groovy/ast/tools/MethodNodeUtils.java b/src/main/java/org/apache/groovy/ast/tools/MethodNodeUtils.java
index 2e9c122..f49400d 100644
--- a/src/main/java/org/apache/groovy/ast/tools/MethodNodeUtils.java
+++ b/src/main/java/org/apache/groovy/ast/tools/MethodNodeUtils.java
@@ -18,13 +18,14 @@
  */
 package org.apache.groovy.ast.tools;
 
-import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.ast.stmt.BlockStatement;
 import org.codehaus.groovy.ast.stmt.Statement;
 
 import static org.apache.groovy.util.BeanUtils.decapitalize;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 
 /**
  * Utility class for working with MethodNodes
@@ -85,17 +86,17 @@ public class MethodNodeUtils {
         if (nameLength > 2) {
             switch (name.charAt(0)) {
                 case 'g':
-                    if (nameLength > 3 && name.charAt(1) == 'e' && name.charAt(2) == 't' && mNode.getParameters().length == 0 && !mNode.getReturnType().equals(ClassHelper.VOID_TYPE)) {
+                    if (nameLength > 3 && name.charAt(1) == 'e' && name.charAt(2) == 't' && mNode.getParameters().length == 0 && !isPrimitiveVoid(mNode.getReturnType())) {
                         return decapitalize(name.substring(3));
                     }
                     break;
                 case 's':
-                    if (nameLength > 3 && name.charAt(1) == 'e' && name.charAt(2) == 't' && mNode.getParameters().length == 1 /*&& mNode.getReturnType().equals(ClassHelper.VOID_TYPE)*/) {
+                    if (nameLength > 3 && name.charAt(1) == 'e' && name.charAt(2) == 't' && mNode.getParameters().length == 1 /*&& isPrimitiveVoid(mNode.getReturnType())*/) {
                         return decapitalize(name.substring(3));
                     }
                     break;
                 case 'i':
-                    if (name.charAt(1) == 's' && mNode.getParameters().length == 0 && (mNode.getReturnType().equals(ClassHelper.boolean_TYPE) /*|| mNode.getReturnType().equals(ClassHelper.Boolean_TYPE)*/)) {
+                    if (name.charAt(1) == 's' && mNode.getParameters().length == 0 && (isPrimitiveBoolean(mNode.getReturnType()) /*|| mNode.getReturnType().equals(ClassHelper.Boolean_TYPE)*/)) {
                         return decapitalize(name.substring(2));
                     }
                     break;
diff --git a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
index 1fe7595..31194c9 100644
--- a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
+++ b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
@@ -343,6 +343,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
 import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveType;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.last;
 
@@ -873,7 +874,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
     @Override
     public List<ClassNode> visitCatchType(final CatchTypeContext ctx) {
         if (!asBoolean(ctx)) {
-            return Collections.singletonList(ClassHelper.OBJECT_TYPE);
+            return Collections.singletonList(ClassHelper.DYNAMIC_TYPE);
         }
 
         return ctx.qualifiedClassName().stream()
@@ -1152,13 +1153,13 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
                     outerClass,
                     outerClass.getName() + "$" + className,
                     modifiers,
-                    ClassHelper.OBJECT_TYPE
+                    ClassHelper.OBJECT_TYPE.getPlainNodeReference()
             );
         } else {
             classNode = new ClassNode(
                     packageName + className,
                     modifiers,
-                    ClassHelper.OBJECT_TYPE
+                    ClassHelper.OBJECT_TYPE.getPlainNodeReference()
             );
         }
 
@@ -1186,7 +1187,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
                 }
                 superClass = scs[0];
             } else {
-                superClass = ClassHelper.OBJECT_TYPE;
+                superClass = ClassHelper.OBJECT_TYPE.getPlainNodeReference();
             }
             classNode.setSuperClass(superClass);
             classNode.setInterfaces(this.visitTypeList(ctx.is));
@@ -1194,7 +1195,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
 
         } else if (isInterface) {
             classNode.setModifiers(classNode.getModifiers() | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT);
-            classNode.setSuperClass(ClassHelper.OBJECT_TYPE);
+            classNode.setSuperClass(ClassHelper.OBJECT_TYPE.getPlainNodeReference());
             classNode.setInterfaces(this.visitTypeList(ctx.scs));
             this.initUsingGenerics(classNode);
             this.hackMixins(classNode);
@@ -1442,13 +1443,10 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
 
     @Override
     public GenericsType visitTypeParameter(final TypeParameterContext ctx) {
-        return configureAST(
-                new GenericsType(
-                        configureAST(ClassHelper.make(this.visitClassName(ctx.className())), ctx),
-                        this.visitTypeBound(ctx.typeBound()),
-                        null
-                ),
-                ctx);
+        ClassNode baseType = configureAST(ClassHelper.make(this.visitClassName(ctx.className())), ctx);
+        baseType.addTypeAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt()));
+        GenericsType genericsType = new GenericsType(baseType, this.visitTypeBound(ctx.typeBound()), null);
+        return configureAST(genericsType, ctx);
     }
 
     @Override
@@ -1708,7 +1706,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
     @Override
     public ClassNode visitReturnType(final ReturnTypeContext ctx) {
         if (!asBoolean(ctx)) {
-            return ClassHelper.OBJECT_TYPE;
+            return ClassHelper.OBJECT_TYPE.getPlainNodeReference();
         }
 
         if (asBoolean(ctx.type())) {
@@ -1717,10 +1715,10 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
 
         if (asBoolean(ctx.VOID())) {
             if (3 == ctx.ct) { // annotation
-                throw createParsingFailedException("annotation method can not have void return type", ctx);
+                throw createParsingFailedException("annotation method cannot have void return type", ctx);
             }
 
-            return ClassHelper.VOID_TYPE;
+            return ClassHelper.VOID_TYPE.getPlainNodeReference(false);
         }
 
         throw createParsingFailedException("Unsupported return type: " + ctx.getText(), ctx);
@@ -3635,7 +3633,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
     public Parameter[] visitStandardLambdaParameters(final StandardLambdaParametersContext ctx) {
         if (asBoolean(ctx.variableDeclaratorId())) {
             VariableExpression variable = this.visitVariableDeclaratorId(ctx.variableDeclaratorId());
-            Parameter parameter = new Parameter(ClassHelper.OBJECT_TYPE, variable.getName());
+            Parameter parameter = new Parameter(ClassHelper.OBJECT_TYPE.getPlainNodeReference(), variable.getName());
             configureAST(parameter, variable);
             return new Parameter[]{parameter};
         }
@@ -3861,7 +3859,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
     @Override
     public ClassNode visitType(final TypeContext ctx) {
         if (!asBoolean(ctx)) {
-            return ClassHelper.OBJECT_TYPE;
+            return ClassHelper.OBJECT_TYPE.getPlainNodeReference();
         }
 
         ClassNode classNode = null;
@@ -3881,7 +3879,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
             throw createParsingFailedException("Unsupported type: " + ctx.getText(), ctx);
         }
 
-        classNode.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt()));
+        classNode.addTypeAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt()));
 
         List<List<AnnotationNode>> dimList = this.visitEmptyDimsOpt(ctx.emptyDimsOpt());
         if (asBoolean(dimList)) {
@@ -3932,8 +3930,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
     public GenericsType visitTypeArgument(final TypeArgumentContext ctx) {
         if (asBoolean(ctx.QUESTION())) {
             ClassNode baseType = configureAST(ClassHelper.makeWithoutCaching(QUESTION_STR), ctx.QUESTION());
-
-            baseType.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt()));
+            baseType.addTypeAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt()));
 
             if (!asBoolean(ctx.type())) {
                 GenericsType genericsType = new GenericsType(baseType);
@@ -3958,10 +3955,8 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
 
             return configureAST(genericsType, ctx);
         } else if (asBoolean(ctx.type())) {
-            return configureAST(
-                    this.createGenericsType(
-                            this.visitType(ctx.type())),
-                    ctx);
+            ClassNode baseType = configureAST(this.visitType(ctx.type()), ctx);
+            return configureAST(this.createGenericsType(baseType), ctx);
         }
 
         throw createParsingFailedException("Unsupported type argument: " + ctx.getText(), ctx);
@@ -3969,7 +3964,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
 
     @Override
     public ClassNode visitPrimitiveType(final PrimitiveTypeContext ctx) {
-        return configureAST(ClassHelper.make(ctx.getText()), ctx);
+        return configureAST(ClassHelper.make(ctx.getText()).getPlainNodeReference(false), ctx);
     }
 
     // } type ------------------------------------------------------------------
@@ -4156,7 +4151,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
     public ClassNode visitAnnotatedQualifiedClassName(final AnnotatedQualifiedClassNameContext ctx) {
         ClassNode classNode = this.visitQualifiedClassName(ctx.qualifiedClassName());
 
-        classNode.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt()));
+        classNode.addTypeAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt()));
 
         return classNode;
     }
@@ -4192,7 +4187,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
     }
 
     private ClassNode createArrayType(final ClassNode elementType) {
-        if (ClassHelper.VOID_TYPE.equals(elementType)) {
+        if (isPrimitiveVoid(elementType)) {
             throw this.createParsingFailedException("void[] is an invalid type", elementType);
         }
         return elementType.makeArray();
diff --git a/src/main/java/org/codehaus/groovy/antlr/PrimitiveHelper.java b/src/main/java/org/codehaus/groovy/antlr/PrimitiveHelper.java
index 22b8520..22b7513 100644
--- a/src/main/java/org/codehaus/groovy/antlr/PrimitiveHelper.java
+++ b/src/main/java/org/codehaus/groovy/antlr/PrimitiveHelper.java
@@ -18,38 +18,46 @@
  */
 package org.codehaus.groovy.antlr;
 
-import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.expr.ConstantExpression;
 import org.codehaus.groovy.ast.expr.Expression;
 
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveByte;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveChar;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveFloat;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveInt;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveShort;
+
 public class PrimitiveHelper {
     private PrimitiveHelper() {
     }
 
     public static Expression getDefaultValueForPrimitive(ClassNode type) {
-        if (type == ClassHelper.int_TYPE) {
+        if (isPrimitiveInt(type)) {
             return new ConstantExpression(0);
         }
-        if (type == ClassHelper.long_TYPE) {
+        if (isPrimitiveLong(type)) {
             return new ConstantExpression(0L);
         }
-        if (type == ClassHelper.double_TYPE) {
+        if (isPrimitiveDouble(type)) {
             return new ConstantExpression(0.0);
         }
-        if (type == ClassHelper.float_TYPE) {
+        if (isPrimitiveFloat(type)) {
             return new ConstantExpression(0.0F);
         }
-        if (type == ClassHelper.boolean_TYPE) {
+        if (isPrimitiveBoolean(type)) {
             return ConstantExpression.FALSE;
         }
-        if (type == ClassHelper.short_TYPE) {
+        if (isPrimitiveShort(type)) {
             return new ConstantExpression((short) 0);
         }
-        if (type == ClassHelper.byte_TYPE) {
+        if (isPrimitiveByte(type)) {
             return new ConstantExpression((byte) 0);
         }
-        if (type == ClassHelper.char_TYPE) {
+        if (isPrimitiveChar(type)) {
             return new ConstantExpression((char) 0);
         }
         return null;
diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
index ae74efe..869e9f1 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
@@ -71,6 +71,14 @@ import java.util.Map;
 import java.util.Set;
 import java.util.regex.Pattern;
 
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveByte;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveChar;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveFloat;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveInt;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveShort;
+
 /**
  * Helper for {@link ClassNode} and classes handling them.  Contains a set of
  * pre-defined instances for the most used types and some code for cached node
@@ -322,6 +330,9 @@ public class ClassHelper {
         if (!isPrimitiveType(cn)) return cn;
 
         ClassNode result = PRIMITIVE_TYPE_TO_WRAPPER_TYPE_MAP.get(cn);
+        if (result == null) {
+            result = PRIMITIVE_TYPE_TO_WRAPPER_TYPE_MAP.get(cn.redirect());
+        }
 
         if (null != result) {
             return result;
@@ -373,31 +384,31 @@ public class ClassHelper {
      */
     public static boolean isStaticConstantInitializerType(ClassNode cn) {
         cn = cn.redirect();
-        return cn == int_TYPE ||
-                cn == float_TYPE ||
-                cn == long_TYPE ||
-                cn == double_TYPE ||
-                cn == STRING_TYPE ||
+        return isPrimitiveInt(cn) ||
+                isPrimitiveFloat(cn) ||
+                isPrimitiveLong(cn) ||
+                isPrimitiveDouble(cn) ||
+                cn.equals(STRING_TYPE) ||
                 // the next items require conversion to int when initializing
-                cn == byte_TYPE ||
-                cn == char_TYPE ||
-                cn == short_TYPE;
+                isPrimitiveByte(cn) ||
+                isPrimitiveChar(cn) ||
+                isPrimitiveShort(cn);
     }
 
     public static boolean isNumberType(ClassNode cn) {
         cn = cn.redirect();
-        return cn == Byte_TYPE ||
-                cn == Short_TYPE ||
-                cn == Integer_TYPE ||
-                cn == Long_TYPE ||
-                cn == Float_TYPE ||
-                cn == Double_TYPE ||
-                cn == byte_TYPE ||
-                cn == short_TYPE ||
-                cn == int_TYPE ||
-                cn == long_TYPE ||
-                cn == float_TYPE ||
-                cn == double_TYPE;
+        return cn.equals(Byte_TYPE) ||
+                cn.equals(Short_TYPE) ||
+                cn.equals(Integer_TYPE) ||
+                cn.equals(Long_TYPE) ||
+                cn.equals(Float_TYPE) ||
+                cn.equals(Double_TYPE) ||
+                isPrimitiveByte(cn) ||
+                isPrimitiveShort(cn) ||
+                isPrimitiveInt(cn) ||
+                isPrimitiveLong(cn) ||
+                isPrimitiveFloat(cn) ||
+                isPrimitiveDouble(cn);
     }
 
     public static ClassNode makeReference() {
diff --git a/src/main/java/org/codehaus/groovy/ast/ClassNode.java b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
index 6eaa597..22d0e70 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
@@ -50,6 +50,7 @@ import java.util.stream.Collectors;
 
 import static java.util.Arrays.stream;
 import static org.apache.groovy.ast.tools.MethodNodeUtils.getCodeAsBlock;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
 import static org.objectweb.asm.Opcodes.ACC_ANNOTATION;
 import static org.objectweb.asm.Opcodes.ACC_ENUM;
@@ -158,6 +159,7 @@ public class ClassNode extends AnnotatedNode {
     private ClassNode superClass;
     protected boolean isPrimaryNode;
     protected List<InnerClassNode> innerClasses;
+    private List<AnnotationNode> typeAnnotations = Collections.emptyList();
 
     /**
      * The AST Transformations to be applied during compilation.
@@ -909,8 +911,8 @@ public class ClassNode extends AnnotatedNode {
      * @return true if this node is derived from the given ClassNode
      */
     public boolean isDerivedFrom(ClassNode type) {
-        if (this.equals(ClassHelper.VOID_TYPE)) {
-            return type.equals(ClassHelper.VOID_TYPE);
+        if (isPrimitiveVoid(this)) {
+            return isPrimitiveVoid(type);
         }
         if (type.equals(ClassHelper.OBJECT_TYPE)) {
             return true;
@@ -1160,7 +1162,7 @@ public class ClassNode extends AnnotatedNode {
     public MethodNode getSetterMethod(String setterName, boolean voidOnly) {
         for (MethodNode method : getDeclaredMethods(setterName)) {
             if (setterName.equals(method.getName())
-                    && (!voidOnly || ClassHelper.VOID_TYPE == method.getReturnType())
+                    && (!voidOnly || ClassHelper.VOID_TYPE.equals(method.getReturnType()))
                     && method.getParameters().length == 1) {
                 return method;
             }
@@ -1420,8 +1422,8 @@ public class ClassNode extends AnnotatedNode {
         this.usesGenerics = usesGenerics;
     }
 
-    public ClassNode getPlainNodeReference() {
-        if (ClassHelper.isPrimitiveType(this)) return this;
+    public ClassNode getPlainNodeReference(boolean skipPrimitives) {
+        if (skipPrimitives && ClassHelper.isPrimitiveType(this)) return this;
         ClassNode n = new ClassNode(name, modifiers, superClass, null, null);
         n.isPrimaryNode = false;
         n.setRedirect(redirect());
@@ -1431,6 +1433,10 @@ public class ClassNode extends AnnotatedNode {
         return n;
     }
 
+    public ClassNode getPlainNodeReference() {
+        return getPlainNodeReference(true);
+    }
+
     public boolean isAnnotationDefinition() {
         return isInterface() && (getModifiers() & ACC_ANNOTATION) != 0;
     }
@@ -1506,4 +1512,33 @@ public class ClassNode extends AnnotatedNode {
     public String getText() {
         return getName();
     }
+
+    public List<AnnotationNode> getTypeAnnotations() {
+        return typeAnnotations;
+    }
+
+    public List<AnnotationNode> getTypeAnnotations(ClassNode type) {
+        List<AnnotationNode> ret = new ArrayList<>(typeAnnotations.size());
+        for (AnnotationNode node : typeAnnotations) {
+            if (type.equals(node.getClassNode())) {
+                ret.add(node);
+            }
+        }
+        return ret;
+    }
+
+    public void addTypeAnnotation(AnnotationNode annotation) {
+        if (annotation != null) {
+            if (typeAnnotations == Collections.EMPTY_LIST) {
+                typeAnnotations = new ArrayList<>(3);
+            }
+            typeAnnotations.add(annotation);
+        }
+    }
+
+    public void addTypeAnnotations(List<AnnotationNode> annotations) {
+        for (AnnotationNode annotation : annotations) {
+            addTypeAnnotation(annotation);
+        }
+    }
 }
diff --git a/src/main/java/org/codehaus/groovy/ast/FieldNode.java b/src/main/java/org/codehaus/groovy/ast/FieldNode.java
index 64b2090..ef11395 100644
--- a/src/main/java/org/codehaus/groovy/ast/FieldNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/FieldNode.java
@@ -19,6 +19,7 @@
 package org.codehaus.groovy.ast;
 
 import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.classgen.asm.util.TypeUtil;
 
 import java.lang.reflect.Field;
 
@@ -83,7 +84,7 @@ public class FieldNode extends AnnotatedNode implements Variable {
     public void setType(ClassNode type) {
         this.type = type;
         this.originType = type;
-        dynamicTyped |= type == ClassHelper.DYNAMIC_TYPE;
+        dynamicTyped |= TypeUtil.isDynamicTyped(type);
     }
 
     public ClassNode getOwner() {
diff --git a/src/main/java/org/codehaus/groovy/ast/MethodNode.java b/src/main/java/org/codehaus/groovy/ast/MethodNode.java
index 9b4a3cb..0e57a95 100644
--- a/src/main/java/org/codehaus/groovy/ast/MethodNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/MethodNode.java
@@ -21,8 +21,10 @@ package org.codehaus.groovy.ast;
 import org.apache.groovy.ast.tools.MethodNodeUtils;
 import org.codehaus.groovy.ast.stmt.BlockStatement;
 import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.classgen.asm.util.TypeUtil;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 
 import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
@@ -142,7 +144,7 @@ public class MethodNode extends AnnotatedNode {
 
     public void setReturnType(ClassNode returnType) {
         invalidateCachedData();
-        this.dynamicReturnType |= ClassHelper.DYNAMIC_TYPE == returnType;
+        this.dynamicReturnType |= TypeUtil.isDynamicTyped(returnType);
         this.returnType = returnType != null ? returnType : ClassHelper.OBJECT_TYPE;
     }
 
diff --git a/src/main/java/org/codehaus/groovy/ast/ModuleNode.java b/src/main/java/org/codehaus/groovy/ast/ModuleNode.java
index fedec5a..6fcdaec 100644
--- a/src/main/java/org/codehaus/groovy/ast/ModuleNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/ModuleNode.java
@@ -46,6 +46,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
 import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
@@ -412,7 +413,7 @@ public class ModuleNode extends ASTNode {
                     ClassNode retType = node.getReturnType();
 
                     argTypeMatches = (argType.equals(ClassHelper.OBJECT_TYPE) || argType.getName().contains("String[]"));
-                    retTypeMatches = (retType == ClassHelper.VOID_TYPE || retType == ClassHelper.OBJECT_TYPE);
+                    retTypeMatches = (isPrimitiveVoid(retType) || retType.equals(ClassHelper.OBJECT_TYPE));
                     if (retTypeMatches && argTypeMatches) {
                         if (found) {
                             throw new RuntimeException("Repetitive main method found.");
diff --git a/src/main/java/org/codehaus/groovy/ast/Parameter.java b/src/main/java/org/codehaus/groovy/ast/Parameter.java
index e9035e4..cf12123 100644
--- a/src/main/java/org/codehaus/groovy/ast/Parameter.java
+++ b/src/main/java/org/codehaus/groovy/ast/Parameter.java
@@ -19,6 +19,7 @@
 package org.codehaus.groovy.ast;
 
 import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.classgen.asm.util.TypeUtil;
 
 /**
  * Represents a parameter on a constructor or method call. The type name is
@@ -68,7 +69,7 @@ public class Parameter extends AnnotatedNode implements Variable {
 
     public void setType(ClassNode type) {
         this.type = type;
-        dynamicTyped = (dynamicTyped || type == ClassHelper.DYNAMIC_TYPE);
+        dynamicTyped = dynamicTyped || TypeUtil.isDynamicTyped(type);
     }
 
     @Override
diff --git a/src/main/java/org/codehaus/groovy/ast/expr/VariableExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/VariableExpression.java
index 7a83054..35fd400 100644
--- a/src/main/java/org/codehaus/groovy/ast/expr/VariableExpression.java
+++ b/src/main/java/org/codehaus/groovy/ast/expr/VariableExpression.java
@@ -22,6 +22,7 @@ import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.GroovyCodeVisitor;
 import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.classgen.asm.util.TypeUtil;
 
 /**
  * Represents a local variable name, the simplest form of expression. e.g.&#160;"foo".
@@ -36,10 +37,10 @@ public class VariableExpression extends Expression implements Variable {
     private final String variable;
     private int modifiers;
     private boolean inStaticContext;
-    private boolean isDynamicTyped=false;
+    private boolean isDynamicTyped = false;
     private Variable accessedVariable;
-    boolean closureShare=false;
-    boolean useRef=false;
+    boolean closureShare = false;
+    boolean useRef = false;
     private final ClassNode originType;
 
     public Variable getAccessedVariable() {
@@ -103,7 +104,7 @@ public class VariableExpression extends Expression implements Variable {
 
     @Override
     public boolean isInStaticContext() {
-        if (accessedVariable!=null && accessedVariable!=this) return accessedVariable.isInStaticContext();
+        if (accessedVariable != null && accessedVariable != this) return accessedVariable.isInStaticContext();
         return inStaticContext;
     }
 
@@ -116,17 +117,18 @@ public class VariableExpression extends Expression implements Variable {
      * the {@link #getAccessedVariable() accessed variable} is ({@link #isClosureSharedVariable() shared},
      * this operation is unsafe and may lead to a verify error at compile time. Instead, set the type of
      * the {@link #getAccessedVariable() accessed variable}
+     *
      * @param cn the type to be set on this variable
      */
     @Override
-    public void setType(ClassNode cn){
+    public void setType(ClassNode cn) {
         super.setType(cn);
-        isDynamicTyped |= ClassHelper.DYNAMIC_TYPE==cn;
+        isDynamicTyped |= TypeUtil.isDynamicTyped(cn);
     }
 
     @Override
     public boolean isDynamicTyped() {
-        if (accessedVariable!=null && accessedVariable!=this) return accessedVariable.isDynamicTyped();
+        if (accessedVariable != null && accessedVariable != this) return accessedVariable.isDynamicTyped();
         return isDynamicTyped;
     }
 
@@ -137,11 +139,12 @@ public class VariableExpression extends Expression implements Variable {
      * def cl = { println str }
      * </pre>
      * The "str" variable is closure shared.
+     *
      * @return true if this variable is used in a closure
      */
     @Override
     public boolean isClosureSharedVariable() {
-        if (accessedVariable!=null && accessedVariable!=this) return accessedVariable.isClosureSharedVariable();
+        if (accessedVariable != null && accessedVariable != this) return accessedVariable.isClosureSharedVariable();
         return closureShare;
     }
 
@@ -152,6 +155,7 @@ public class VariableExpression extends Expression implements Variable {
      * </pre>
      * The "str" variable is closure shared. The variable expression inside the closure references an
      * accessed variable "str" which must have the closure shared flag set.
+     *
      * @param inClosure tells if this variable is later referenced in a closure
      */
     @Override
@@ -167,6 +171,7 @@ public class VariableExpression extends Expression implements Variable {
     /**
      * For internal use only. This flag is used by compiler internals and should probably
      * be converted to a node metadata in future.
+     *
      * @param useRef
      */
     public void setUseReferenceDirectly(boolean useRef) {
@@ -183,18 +188,19 @@ public class VariableExpression extends Expression implements Variable {
 
     @Override
     public ClassNode getType() {
-        if (accessedVariable!=null && accessedVariable!=this) return accessedVariable.getType();
+        if (accessedVariable != null && accessedVariable != this) return accessedVariable.getType();
         return super.getType();
     }
 
     /**
      * Returns the type which was used when this variable expression was created. For example,
      * {@link #getType()} may return a boxed type while this method would return the primitive type.
+     *
      * @return the type which was used to define this variable expression
      */
     @Override
     public ClassNode getOriginType() {
-        if (accessedVariable!=null && accessedVariable!=this) return accessedVariable.getOriginType();
+        if (accessedVariable != null && accessedVariable != this) return accessedVariable.getOriginType();
         return originType;
     }
 
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/BeanUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/BeanUtils.java
index e2377ba..aaa9be5 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/BeanUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/BeanUtils.java
@@ -32,6 +32,7 @@ import java.util.Set;
 
 import static org.apache.groovy.ast.tools.AnnotatedNodeUtils.hasAnnotation;
 import static org.apache.groovy.util.BeanUtils.decapitalize;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
 
 public class BeanUtils {
     static final String GET_PREFIX = "get";
@@ -120,7 +121,7 @@ public class BeanUtils {
                     // Simple getter
                     propName = decapitalize(name.substring(3));
                     getter = mNode.getCode();
-                } else if (includePseudoGetters && name.startsWith(IS_PREFIX) && paramType.equals(ClassHelper.boolean_TYPE)) {
+                } else if (includePseudoGetters && name.startsWith(IS_PREFIX) && isPrimitiveBoolean(paramType)) {
                     // boolean getter
                     propName = decapitalize(name.substring(2));
                     getter = mNode.getCode();
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
index 32b1477..1673b4a 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
@@ -465,7 +465,7 @@ public class GeneralUtils {
 
     public static List<FieldNode> getSuperNonPropertyFields(final ClassNode cNode) {
         List<FieldNode> result;
-        if (cNode == ClassHelper.OBJECT_TYPE) {
+        if (cNode.equals(ClassHelper.OBJECT_TYPE)) {
             result = new ArrayList<>();
         } else {
             result = getSuperNonPropertyFields(cNode.getSuperClass());
@@ -480,7 +480,7 @@ public class GeneralUtils {
 
     public static List<FieldNode> getSuperPropertyFields(final ClassNode cNode) {
         List<FieldNode> result;
-        if (cNode == ClassHelper.OBJECT_TYPE) {
+        if (cNode.equals(ClassHelper.OBJECT_TYPE)) {
             result = new ArrayList<>();
         } else {
             result = getSuperPropertyFields(cNode.getSuperClass());
@@ -505,7 +505,7 @@ public class GeneralUtils {
                                                       final boolean includeFields, final boolean includePseudoGetters, final boolean includePseudoSetters,
                                                       final boolean traverseSuperClasses, final boolean skipReadonly, final boolean reverse, final boolean allNames, final boolean includeStatic) {
         List<PropertyNode> result = new ArrayList<>();
-        if (cNode != ClassHelper.OBJECT_TYPE && traverseSuperClasses && !reverse) {
+        if (!(cNode.equals(ClassHelper.OBJECT_TYPE)) && traverseSuperClasses && !reverse) {
             result.addAll(getAllProperties(names, origType, cNode.getSuperClass(), includeProperties, includeFields, includePseudoGetters, includePseudoSetters, true, skipReadonly));
         }
         if (includeProperties) {
@@ -540,7 +540,7 @@ public class GeneralUtils {
                 names.add(fNode.getName());
             }
         }
-        if (cNode != ClassHelper.OBJECT_TYPE && traverseSuperClasses && reverse) {
+        if (!(cNode.equals(ClassHelper.OBJECT_TYPE)) && traverseSuperClasses && reverse) {
             result.addAll(getAllProperties(names, origType, cNode.getSuperClass(), includeProperties, includeFields, includePseudoGetters, includePseudoSetters, true, skipReadonly));
         }
         return result;
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index 1d66547..5ec2c18 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -388,7 +388,7 @@ public class GenericsUtils {
                 }
             }
         }
-        if (type == null) type = ClassHelper.OBJECT_TYPE;
+        if (type == null) type = ClassHelper.OBJECT_TYPE.getPlainNodeReference();
         GenericsType[] oldgTypes = type.getGenericsTypes();
         GenericsType[] newgTypes = EMPTY_GENERICS_ARRAY;
         if (oldgTypes != null) {
@@ -453,7 +453,7 @@ public class GenericsUtils {
                 return correctToGenericsSpec(genericsSpec, type);
             }
         }
-        if (type == null) type = ClassHelper.OBJECT_TYPE;
+        if (type == null) type = ClassHelper.OBJECT_TYPE.getPlainNodeReference();
         return type;
     }
 
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java b/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
index 6f15e6b..608cced 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
@@ -41,17 +41,18 @@ import static org.codehaus.groovy.ast.ClassHelper.BigInteger_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.Number_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.byte_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.char_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.double_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.float_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.getUnwrapper;
 import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
-import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.isNumberType;
 import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
-import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveByte;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveChar;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveFloat;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveInt;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveShort;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
 
@@ -108,7 +109,7 @@ public class WideningCategories {
      * @param type the type to check
      */
     public static boolean isInt(ClassNode type) {
-        return int_TYPE == type;
+        return isPrimitiveInt(type);
     }
 
     /**
@@ -116,7 +117,7 @@ public class WideningCategories {
      * @param type the type to check
      */
     public static boolean isDouble(ClassNode type) {
-        return double_TYPE == type;
+        return isPrimitiveDouble(type);
     }
 
     /**
@@ -124,7 +125,7 @@ public class WideningCategories {
      * @param type the type to check
      */
     public static boolean isFloat(ClassNode type) {
-        return float_TYPE == type;
+        return isPrimitiveFloat(type);
     }
 
     /**
@@ -132,37 +133,39 @@ public class WideningCategories {
      * byte, char, short, int.
      */
     public static boolean isIntCategory(ClassNode type) {
-        return  type==byte_TYPE     ||  type==char_TYPE     ||
-                type==int_TYPE      ||  type==short_TYPE;
+        return isPrimitiveByte(type) || isPrimitiveChar(type) || isPrimitiveInt(type) || isPrimitiveShort(type);
     }
+
     /**
      * It is of a long category, if the provided type is a
      * long, its wrapper or if it is a long category.
      */
     public static boolean isLongCategory(ClassNode type) {
-        return  type==long_TYPE     ||  isIntCategory(type);
+        return isPrimitiveLong(type) || isIntCategory(type);
     }
+
     /**
      * It is of a BigInteger category, if the provided type is a
      * long category or a BigInteger.
      */
     public static boolean isBigIntCategory(ClassNode type) {
-        return  type==BigInteger_TYPE || isLongCategory(type);
+        return type.equals(BigInteger_TYPE) || isLongCategory(type);
     }
+
     /**
      * It is of a BigDecimal category, if the provided type is a
      * BigInteger category or a BigDecimal.
      */
     public static boolean isBigDecCategory(ClassNode type) {
-        return  type==BigDecimal_TYPE || isBigIntCategory(type);
+        return type.equals(BigDecimal_TYPE) || isBigIntCategory(type);
     }
+
     /**
      * It is of a double category, if the provided type is a
      * BigDecimal, a float, double. C(type)=double
      */
     public static boolean isDoubleCategory(ClassNode type) {
-        return  type==float_TYPE    ||  type==double_TYPE   ||
-                isBigDecCategory(type);
+        return isPrimitiveFloat(type) || isPrimitiveDouble(type) || isBigDecCategory(type);
     }
 
     /**
@@ -170,7 +173,7 @@ public class WideningCategories {
      * a float, double. C(type)=float
      */
     public static boolean isFloatingCategory(ClassNode type) {
-        return  type==float_TYPE    ||  type==double_TYPE;
+        return isPrimitiveFloat(type) || isPrimitiveDouble(type);
     }
 
     public static boolean isNumberCategory(ClassNode type) {
@@ -342,7 +345,7 @@ public class WideningCategories {
             }
             return OBJECT_TYPE;
         }
-        if (a.equals(VOID_TYPE) || b.equals(VOID_TYPE)) {
+        if (isPrimitiveVoid(a) || isPrimitiveVoid(b)) {
             if (!b.equals(a)) {
                 // one class is void, the other is not
                 return OBJECT_TYPE;
@@ -559,7 +562,7 @@ public class WideningCategories {
                 name = "Virtual$"+baseType1.getName();
             }
         } else {
-            superClass = OBJECT_TYPE;
+            superClass = OBJECT_TYPE.getPlainNodeReference();
             if (baseType1.isDerivedFrom(baseType2)) {
                 superClass = baseType2;
             } else if (baseType2.isDerivedFrom(baseType1)) {
diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
index f367735..90d3d0a 100644
--- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
+++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
@@ -29,6 +29,7 @@ import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.ConstructorNode;
 import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.GenericsType;
 import org.codehaus.groovy.ast.InnerClassNode;
 import org.codehaus.groovy.ast.InterfaceHelperClassNode;
 import org.codehaus.groovy.ast.MethodNode;
@@ -110,6 +111,8 @@ import org.objectweb.asm.FieldVisitor;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Type;
+import org.objectweb.asm.TypePath;
+import org.objectweb.asm.TypeReference;
 import org.objectweb.asm.util.TraceMethodVisitor;
 
 import java.io.PrintWriter;
@@ -134,6 +137,14 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.getGetterName;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.thisPropX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveByte;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveChar;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveFloat;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveInt;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveShort;
 import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.PROPERTY_OWNER;
 import static org.objectweb.asm.Opcodes.AASTORE;
 import static org.objectweb.asm.Opcodes.ACC_ENUM;
@@ -187,6 +198,18 @@ import static org.objectweb.asm.Opcodes.T_INT;
 import static org.objectweb.asm.Opcodes.T_LONG;
 import static org.objectweb.asm.Opcodes.T_SHORT;
 import static org.objectweb.asm.Opcodes.V_PREVIEW;
+import static org.objectweb.asm.TypeReference.CLASS_TYPE_PARAMETER;
+import static org.objectweb.asm.TypeReference.CLASS_TYPE_PARAMETER_BOUND;
+import static org.objectweb.asm.TypeReference.FIELD;
+import static org.objectweb.asm.TypeReference.METHOD_RETURN;
+import static org.objectweb.asm.TypeReference.METHOD_TYPE_PARAMETER;
+import static org.objectweb.asm.TypeReference.METHOD_TYPE_PARAMETER_BOUND;
+import static org.objectweb.asm.TypeReference.newExceptionReference;
+import static org.objectweb.asm.TypeReference.newFormalParameterReference;
+import static org.objectweb.asm.TypeReference.newSuperTypeReference;
+import static org.objectweb.asm.TypeReference.newTypeParameterBoundReference;
+import static org.objectweb.asm.TypeReference.newTypeParameterReference;
+import static org.objectweb.asm.TypeReference.newTypeReference;
 
 /**
  * Generates Java class versions of Groovy classes using ASM.
@@ -309,6 +332,12 @@ public class AsmClassGenerator extends ClassGenerator {
                 }
             } else {
                 visitAnnotations(classNode, classVisitor);
+                visitTypeParameters(classNode, classVisitor);
+                visitType(classNode.getUnresolvedSuperClass(), classVisitor, newSuperTypeReference(-1), "", true);
+                ClassNode[] interfaces = classNode.getInterfaces();
+                for (int i = 0; i < interfaces.length; i++) {
+                    visitType(interfaces[i], classVisitor, newSuperTypeReference(i), "", true);
+                }
                 if (classNode.isInterface()) {
                     String outerClassName = classNode.getName();
                     String name = outerClassName + "$" + context.getNextInnerClassIdx();
@@ -436,8 +465,10 @@ public class AsmClassGenerator extends ClassGenerator {
         controller.resetLineNumber();
 
         visitAnnotations(node, mv);
-        for (int i = 0, n = parameters.length; i < n; i += 1) {
-            visitParameterAnnotations(parameters[i], i, mv);
+        visitTypeParameters(node, mv);
+        // ideally following statement would be in visitMethod but mv not visible there
+        if (!(node instanceof ConstructorNode)) {
+            visitType(node.getReturnType(), mv, newTypeReference(METHOD_RETURN), "", true);
         }
 
         // add parameter names to the MethodVisitor (JDK8+)
@@ -447,6 +478,20 @@ public class AsmClassGenerator extends ClassGenerator {
                 mv.visitParameter(parameter.getName(), parameter.getModifiers());
             }
         }
+        for (int i = 0, n = parameters.length; i < n; i += 1) {
+            visitParameterAnnotations(parameters[i], i, mv);
+            ClassNode paramType = parameters[i].getType();
+            if (paramType.isGenericsPlaceHolder()) {
+                visitTypeAnnotations(paramType, mv, newFormalParameterReference(i), "", true);
+            } else {
+                visitType(parameters[i].getType(), mv, newFormalParameterReference(i), "", true);
+            }
+        }
+        if (node.getExceptions() != null) {
+            for (int i = 0, n = node.getExceptions().length; i < n; i += 1) {
+                visitTypeAnnotations(node.getExceptions()[i], mv, newExceptionReference(i), "", true);
+            }
+        }
 
         if (controller.getClassNode().isAnnotationDefinition() && !node.isStaticConstructor()) {
             visitAnnotationDefault(node, mv);
@@ -630,9 +675,9 @@ public class AsmClassGenerator extends ClassGenerator {
                 ? cexp.getValue() : null; // GROOVY-5150
         if (value != null) {
             // byte, char and short require an extra cast
-            if (ClassHelper.byte_TYPE.equals(t) || ClassHelper.short_TYPE.equals(t)) {
+            if (isPrimitiveByte(t) || isPrimitiveShort(t)) {
                 value = ((Number) value).intValue();
-            } else if (ClassHelper.char_TYPE.equals(t)) {
+            } else if (isPrimitiveChar(t)) {
                 value = Integer.valueOf((Character)value);
             }
         }
@@ -643,6 +688,7 @@ public class AsmClassGenerator extends ClassGenerator {
                 signature,
                 value);
         visitAnnotations(fieldNode, fv);
+        visitType(fieldNode.getType(), fv, newTypeReference(FIELD), "", true);
         fv.visitEnd();
     }
 
@@ -1670,28 +1716,28 @@ public class AsmClassGenerator extends ClassGenerator {
         if (!elementType.isArray() || expression.hasInitializer()) {
             if (ClassHelper.isPrimitiveType(elementType)) {
                 int primType = 0;
-                if (elementType == ClassHelper.boolean_TYPE) {
+                if (isPrimitiveBoolean(elementType)) {
                     primType = T_BOOLEAN;
                     storeIns = BASTORE;
-                } else if (elementType == ClassHelper.char_TYPE) {
+                } else if (isPrimitiveChar(elementType)) {
                     primType = T_CHAR;
                     storeIns = CASTORE;
-                } else if (elementType == ClassHelper.float_TYPE) {
+                } else if (isPrimitiveFloat(elementType)) {
                     primType = T_FLOAT;
                     storeIns = FASTORE;
-                } else if (elementType == ClassHelper.double_TYPE) {
+                } else if (isPrimitiveDouble(elementType)) {
                     primType = T_DOUBLE;
                     storeIns = DASTORE;
-                } else if (elementType == ClassHelper.byte_TYPE) {
+                } else if (isPrimitiveByte(elementType)) {
                     primType = T_BYTE;
                     storeIns = BASTORE;
-                } else if (elementType == ClassHelper.short_TYPE) {
+                } else if (isPrimitiveShort(elementType)) {
                     primType = T_SHORT;
                     storeIns = SASTORE;
-                } else if (elementType == ClassHelper.int_TYPE) {
+                } else if (isPrimitiveInt(elementType)) {
                     primType = T_INT;
                     storeIns = IASTORE;
-                } else if (elementType == ClassHelper.long_TYPE) {
+                } else if (isPrimitiveLong(elementType)) {
                     primType = T_LONG;
                     storeIns = LASTORE;
                 }
@@ -2014,11 +2060,101 @@ public class AsmClassGenerator extends ClassGenerator {
         }
     }
 
+    private void visitTypeAnnotations(final ClassNode sourceNode, final Object visitor, final TypeReference typeRef, final String typePathStr, boolean typeUse) {
+        for (AnnotationNode an : sourceNode.getTypeAnnotations()) {
+            if (an.isBuiltIn() || an.hasSourceRetention()) continue;
+            if (typeUse && !an.isTargetAllowed(AnnotationNode.TYPE_USE_TARGET)) continue;
+
+            AnnotationVisitor av = null;
+            final TypePath typePath;
+            try {
+                typePath = TypePath.fromString(typePathStr);
+            } catch (IllegalArgumentException ex) {
+                throw new GroovyBugError("Illegal type path for " + sourceNode.getText() + ", typeRef = " + typeRef + ", typePath = " + typePathStr);
+            }
+            final int typeRefInt = typeRef.getValue();
+            final String annotationDescriptor = BytecodeHelper.getTypeDescription(an.getClassNode());
+            if (visitor instanceof ClassVisitor) {
+                av = ((ClassVisitor) visitor).visitTypeAnnotation(typeRefInt, typePath, annotationDescriptor, an.hasRuntimeRetention());
+            } else if (visitor instanceof MethodVisitor) {
+                av = ((MethodVisitor) visitor).visitTypeAnnotation(typeRefInt, typePath, annotationDescriptor, an.hasRuntimeRetention());
+            } else if (visitor instanceof FieldVisitor) {
+                av = ((FieldVisitor) visitor).visitTypeAnnotation(typeRefInt, typePath, annotationDescriptor, an.hasRuntimeRetention());
+            } else {
+                throwException("Cannot create an AnnotationVisitor. Please report Groovy bug");
+            }
+            visitAnnotationAttributes(an, av);
+            av.visitEnd();
+        }
+    }
+
+    private void visitGenericsTypeAnnotations(final ClassNode classNode, final Object visitor, final TypeReference typeRef,
+                                              final String typePath, final boolean typeUse) {
+        if (!classNode.isUsingGenerics() || classNode.getGenericsTypes() == null) {
+            return;
+        }
+        visitGenericsTypeAnnotations(classNode.getGenericsTypes(), visitor, typePath, typeRef, typeUse);
+    }
+
+    private void visitTypeParameters(final MethodNode methodNode, final Object visitor) {
+        if (methodNode.getGenericsTypes() == null) {
+            return;
+        }
+        visitGenericsTypeParameterAnnotations(methodNode.getGenericsTypes(), visitor, "", METHOD_TYPE_PARAMETER, METHOD_TYPE_PARAMETER_BOUND);
+    }
+
+    private void visitTypeParameters(final ClassNode classNode, final Object visitor) {
+        if (classNode.getGenericsTypes() == null) {
+            return;
+        }
+        visitGenericsTypeParameterAnnotations(classNode.getGenericsTypes(), visitor, "", CLASS_TYPE_PARAMETER, CLASS_TYPE_PARAMETER_BOUND);
+    }
+
+    private void visitGenericsTypeParameterAnnotations(final GenericsType[] genericsTypes, final Object visitor, final String typePath, final int sort, final int boundSort) {
+        for (int paramIdx = 0; paramIdx < genericsTypes.length; paramIdx++) {
+            GenericsType gt = genericsTypes[paramIdx];
+            visitType(gt.getType(), visitor, newTypeParameterReference(sort, paramIdx), typePath, false);
+            if (gt.getLowerBound() != null) {
+                visitType(gt.getLowerBound(), visitor, newTypeParameterBoundReference(boundSort, paramIdx, 0), typePath, false);
+            }
+            if (gt.getUpperBounds() != null) {
+                ClassNode[] upperBounds = gt.getUpperBounds();
+                for (int boundIdx = 0; boundIdx < upperBounds.length; boundIdx++) {
+                    visitType(upperBounds[boundIdx], visitor, newTypeParameterBoundReference(boundSort, paramIdx, boundIdx), typePath, false);
+                }
+            }
+        }
+    }
+
+    private void visitGenericsTypeAnnotations(final GenericsType[] genericsTypes, final Object visitor,
+                                              final String typePath, final TypeReference typeRef, final boolean typeUse) {
+        for (int paramIdx = 0; paramIdx < genericsTypes.length; paramIdx++) {
+            GenericsType gt = genericsTypes[paramIdx];
+            String prefix = typePath + paramIdx + ";";
+            visitType(gt.getType(), visitor, typeRef, prefix, typeUse);
+            if (!gt.isPlaceholder()) {
+                if (gt.getLowerBound() != null) {
+                    visitType(gt.getLowerBound(), visitor, typeRef, gt.isWildcard() ? prefix + "*" : prefix + "0;", typeUse);
+                }
+                if (gt.getUpperBounds() != null) {
+                    ClassNode[] upperBounds = gt.getUpperBounds();
+                    for (int boundIdx = 0; boundIdx < upperBounds.length; boundIdx++) {
+                        visitType(upperBounds[boundIdx], visitor, typeRef, gt.isWildcard() ? prefix + "*" : prefix + boundIdx + ";", typeUse);
+                    }
+                }
+            }
+        }
+    }
+
+    private void visitType(final ClassNode classNode, final Object visitor, final TypeReference typeRef, final String typePath, boolean typeUse) {
+        visitTypeAnnotations(classNode, visitor, typeRef, typePath, typeUse);
+        visitGenericsTypeAnnotations(classNode, visitor, typeRef, typePath, typeUse);
+    }
+
     private void visitParameterAnnotations(final Parameter parameter, final int paramNumber, final MethodVisitor mv) {
         for (AnnotationNode an : parameter.getAnnotations()) {
             // skip built-in properties
-            if (an.isBuiltIn()) continue;
-            if (an.hasSourceRetention()) continue;
+            if (an.isBuiltIn() || an.hasSourceRetention()) continue;
 
             final String annotationDescriptor = BytecodeHelper.getTypeDescription(an.getClassNode());
             AnnotationVisitor av = mv.visitParameterAnnotation(paramNumber, annotationDescriptor, an.hasRuntimeRetention());
diff --git a/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
index fb26380..aaabcda 100644
--- a/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
@@ -61,7 +61,7 @@ import static java.lang.reflect.Modifier.isStrict;
 import static java.lang.reflect.Modifier.isSynchronized;
 import static java.lang.reflect.Modifier.isTransient;
 import static java.lang.reflect.Modifier.isVolatile;
-import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
 import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
@@ -448,7 +448,7 @@ public class ClassCompletionVerifier extends ClassCodeVisitorSupport {
         checkGenericsUsage(node, node.getParameters());
         checkGenericsUsage(node, node.getReturnType());
         for (Parameter param : node.getParameters()) {
-            if (param.getType().equals(VOID_TYPE)) {
+            if (isPrimitiveVoid(param.getType())) {
                 addError("The " + getDescription(param) + " in " +  getDescription(node) + " has invalid type void", param);
             }
         }
@@ -533,7 +533,7 @@ public class ClassCompletionVerifier extends ClassCodeVisitorSupport {
         checkInterfaceFieldModifiers(node);
         checkInvalidFieldModifiers(node);
         checkGenericsUsage(node, node.getType());
-        if (node.getType().equals(VOID_TYPE)) {
+        if (isPrimitiveVoid(node.getType())) {
             addError("The " + getDescription(node) + " has invalid type void", node);
         }
         super.visitField(node);
@@ -682,7 +682,7 @@ public class ClassCompletionVerifier extends ClassCodeVisitorSupport {
         checkInvalidDeclarationModifier(expression, ACC_SYNCHRONIZED, "synchronized");
         checkInvalidDeclarationModifier(expression, ACC_TRANSIENT, "transient");
         checkInvalidDeclarationModifier(expression, ACC_VOLATILE, "volatile");
-        if (expression.getVariableExpression().getOriginType().equals(VOID_TYPE)) {
+        if (isPrimitiveVoid(expression.getVariableExpression().getOriginType())) {
             addError("The variable '" + expression.getVariableExpression().getName() + "' has invalid type void", expression);
         }
     }
diff --git a/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java b/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
index 4e8ae6b..d5bb759 100644
--- a/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
@@ -25,6 +25,7 @@ import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.ConstructorNode;
 import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.GenericsType;
 import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.PackageNode;
 import org.codehaus.groovy.ast.Parameter;
@@ -48,11 +49,22 @@ import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
+import static org.codehaus.groovy.ast.AnnotationNode.ANNOTATION_TARGET;
+import static org.codehaus.groovy.ast.AnnotationNode.CONSTRUCTOR_TARGET;
+import static org.codehaus.groovy.ast.AnnotationNode.FIELD_TARGET;
+import static org.codehaus.groovy.ast.AnnotationNode.LOCAL_VARIABLE_TARGET;
+import static org.codehaus.groovy.ast.AnnotationNode.METHOD_TARGET;
+import static org.codehaus.groovy.ast.AnnotationNode.PACKAGE_TARGET;
+import static org.codehaus.groovy.ast.AnnotationNode.PARAMETER_TARGET;
+import static org.codehaus.groovy.ast.AnnotationNode.TYPE_PARAMETER_TARGET;
+import static org.codehaus.groovy.ast.AnnotationNode.TYPE_TARGET;
+import static org.codehaus.groovy.ast.AnnotationNode.TYPE_USE_TARGET;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpecRecurse;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
@@ -67,9 +79,11 @@ import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.evalua
  */
 public class ExtendedVerifier extends ClassCodeVisitorSupport {
     public static final String JVM_ERROR_MESSAGE = "Please make sure you are running on a JVM >= 1.5";
+    private static final String EXTENDED_VERIFIER_SEEN = "EXTENDED_VERIFIER_SEEN";
 
     private final SourceUnit source;
     private ClassNode currentClass;
+    private final Map<String, Boolean> repeatableCache = new HashMap<>();
 
     public ExtendedVerifier(SourceUnit sourceUnit) {
         this.source = sourceUnit;
@@ -86,41 +100,115 @@ public class ExtendedVerifier extends ClassCodeVisitorSupport {
         acv.visitClass(node, this.source);
         this.currentClass = node;
         if (node.isAnnotationDefinition()) {
-            visitAnnotations(node, AnnotationNode.ANNOTATION_TARGET);
+            visitAnnotations(node, ANNOTATION_TARGET);
         } else {
-            visitAnnotations(node, AnnotationNode.TYPE_TARGET);
+            visitAnnotations(node, TYPE_TARGET);
+            visitTypeAnnotations(node);
         }
         PackageNode packageNode = node.getPackage();
         if (packageNode != null) {
-            visitAnnotations(packageNode, AnnotationNode.PACKAGE_TARGET);
+            visitAnnotations(packageNode, PACKAGE_TARGET);
+        }
+        visitTypeAnnotations(node.getUnresolvedSuperClass());
+        ClassNode[] interfaces = node.getInterfaces();
+        for (ClassNode anInterface : interfaces) {
+            visitTypeAnnotations(anInterface);
         }
         node.visitContents(this);
     }
 
     @Override
     public void visitField(FieldNode node) {
-        visitAnnotations(node, AnnotationNode.FIELD_TARGET);
+        visitAnnotations(node, FIELD_TARGET);
+        visitTypeAnnotations(node.getType());
+        extractTypeUseAnnotations(node.getAnnotations(), node.getType(), FIELD_TARGET);
     }
 
     @Override
     public void visitDeclarationExpression(DeclarationExpression expression) {
-        visitAnnotations(expression, AnnotationNode.LOCAL_VARIABLE_TARGET);
+        visitAnnotations(expression, LOCAL_VARIABLE_TARGET);
+        visitTypeAnnotations(expression.getType());
     }
 
     @Override
     public void visitConstructor(ConstructorNode node) {
-        visitConstructorOrMethod(node, AnnotationNode.CONSTRUCTOR_TARGET);
+        visitConstructorOrMethod(node, CONSTRUCTOR_TARGET);
+        extractTypeUseAnnotations(node.getAnnotations(), node.getReturnType(), CONSTRUCTOR_TARGET);
     }
 
     @Override
     public void visitMethod(MethodNode node) {
-        visitConstructorOrMethod(node, AnnotationNode.METHOD_TARGET);
+        // by this stage annotations will be resolved so we can determine TYPE_USE ones
+        visitConstructorOrMethod(node, METHOD_TARGET);
+        visitGenericsTypeAnnotations(node);
+        visitTypeAnnotations(node.getReturnType());
+        extractTypeUseAnnotations(node.getAnnotations(), node.getReturnType(), METHOD_TARGET);
+    }
+
+    private void visitTypeAnnotations(ClassNode node) {
+        if (Boolean.TRUE.equals(node.getNodeMetaData(EXTENDED_VERIFIER_SEEN))) return;
+        node.putNodeMetaData(EXTENDED_VERIFIER_SEEN, Boolean.TRUE);
+        visitAnnotations(node, node.getTypeAnnotations(), TYPE_PARAMETER_TARGET);
+        visitGenericsTypeAnnotations(node);
+    }
+
+    private void visitGenericsTypeAnnotations(ClassNode node) {
+        GenericsType[] genericsTypes = node.getGenericsTypes();
+        if (node.isUsingGenerics() && genericsTypes != null) {
+            visitGenericsTypeAnnotations(genericsTypes);
+        }
+    }
+
+    private void visitGenericsTypeAnnotations(MethodNode node) {
+        GenericsType[] genericsTypes = node.getGenericsTypes();
+        if (genericsTypes != null) {
+            visitGenericsTypeAnnotations(genericsTypes);
+        }
+    }
+
+    private void visitGenericsTypeAnnotations(GenericsType[] genericsTypes) {
+        for (GenericsType gt : genericsTypes) {
+            visitTypeAnnotations(gt.getType());
+            if (gt.getLowerBound() != null) {
+                visitTypeAnnotations(gt.getLowerBound());
+            }
+            if (gt.getUpperBounds() != null) {
+                for (ClassNode ub : gt.getUpperBounds()) {
+                    visitTypeAnnotations(ub);
+                }
+            }
+        }
+    }
+
+    private void extractTypeUseAnnotations(List<AnnotationNode> mixed, ClassNode targetType, Integer keepTarget) {
+        List<AnnotationNode> typeUseAnnos = new ArrayList<>();
+        for (AnnotationNode anno : mixed) {
+            if (anno.isTargetAllowed(TYPE_USE_TARGET)) {
+                typeUseAnnos.add(anno);
+            }
+        }
+        if (!typeUseAnnos.isEmpty()) {
+            targetType.addTypeAnnotations(typeUseAnnos);
+            targetType.setAnnotated(true);
+            for (AnnotationNode anno : typeUseAnnos) {
+                if (keepTarget != null && !anno.isTargetAllowed(keepTarget)) {
+                    mixed.remove(anno);
+                }
+            }
+        }
     }
 
     private void visitConstructorOrMethod(MethodNode node, int methodTarget) {
         visitAnnotations(node, methodTarget);
         for (Parameter parameter : node.getParameters()) {
-            visitAnnotations(parameter, AnnotationNode.PARAMETER_TARGET);
+            visitAnnotations(parameter, PARAMETER_TARGET);
+            visitTypeAnnotations(parameter.getType());
+            extractTypeUseAnnotations(parameter.getAnnotations(), parameter.getType(), PARAMETER_TARGET);
+        }
+        if (node.getExceptions() != null) {
+            for (ClassNode t : node.getExceptions()) {
+                visitTypeAnnotations(t);
+            }
         }
 
         if (this.currentClass.isAnnotationDefinition() && !node.isStaticConstructor()) {
@@ -152,7 +240,12 @@ public class ExtendedVerifier extends ClassCodeVisitorSupport {
     }
 
     protected void visitAnnotations(AnnotatedNode node, int target) {
-        if (node.getAnnotations().isEmpty()) {
+        List<AnnotationNode> annotations = node.getAnnotations();
+        visitAnnotations(node, annotations, target);
+    }
+
+    private void visitAnnotations(AnnotatedNode node, List<AnnotationNode> annotations, int target) {
+        if (annotations.isEmpty()) {
             return;
         }
         this.currentClass.setAnnotated(true);
@@ -161,7 +254,7 @@ public class ExtendedVerifier extends ClassCodeVisitorSupport {
             return;
         }
         Map<String, List<AnnotationNode>> nonSourceAnnotations = new LinkedHashMap<>();
-        for (AnnotationNode unvisited : node.getAnnotations()) {
+        for (AnnotationNode unvisited : annotations) {
             AnnotationNode visited;
             {
                 ErrorCollector errorCollector = new ErrorCollector(source.getConfiguration());
@@ -175,7 +268,7 @@ public class ExtendedVerifier extends ClassCodeVisitorSupport {
                 List<AnnotationNode> seen = nonSourceAnnotations.get(name);
                 if (seen == null) {
                     seen = new ArrayList<>();
-                } else if (!isRepeatable(visited.getClassNode())) {
+                } else if (!isRepeatable(visited)) {
                     addError("Cannot specify duplicate annotation on the same member : " + name, visited);
                 }
                 seen.add(visited);
@@ -185,7 +278,7 @@ public class ExtendedVerifier extends ClassCodeVisitorSupport {
             // Check if the annotation target is correct, unless it's the target annotating an annotation definition
             // defining on which target elements the annotation applies
             boolean isTargetAnnotation = name.equals("java.lang.annotation.Target");
-            if (!isTargetAnnotation && !visited.isTargetAllowed(target)) {
+            if (!isTargetAnnotation && !visited.isTargetAllowed(target) && !isTypeUseScenario(visited, target)) {
                 addError("Annotation @" + name + " is not allowed on element " + AnnotationNode.targetToName(target), visited);
             }
             visitDeprecation(node, visited);
@@ -194,13 +287,25 @@ public class ExtendedVerifier extends ClassCodeVisitorSupport {
         processDuplicateAnnotationContainers(node, nonSourceAnnotations);
     }
 
-    private boolean isRepeatable(final ClassNode classNode) {
-        for (AnnotationNode anno : classNode.getAnnotations()) {
-            if (anno.getClassNode().getName().equals("java.lang.annotation.Repeatable")) {
-                return true;
+    private boolean isRepeatable(final AnnotationNode annoNode) {
+        ClassNode annoClassNode = annoNode.getClassNode();
+        String name = annoClassNode.getName();
+        if (!repeatableCache.containsKey(name)) {
+            boolean result = false;
+            for (AnnotationNode anno : annoClassNode.getAnnotations()) {
+                if (anno.getClassNode().getName().equals("java.lang.annotation.Repeatable")) {
+                    result = true;
+                    break;
+                }
             }
+            repeatableCache.put(name, result);
         }
-        return false;
+        return repeatableCache.get(name);
+    }
+
+    private boolean isTypeUseScenario(AnnotationNode visited, int target) {
+        // allow type use everywhere except package
+        return (visited.isTargetAllowed(TYPE_USE_TARGET) && ((target & PACKAGE_TARGET) == 0));
     }
 
     private void processDuplicateAnnotationContainers(AnnotatedNode node, Map<String, List<AnnotationNode>> nonSourceAnnotations) {
diff --git a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
index d90e159..12a9382 100644
--- a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
+++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
@@ -108,7 +108,7 @@ public abstract class InnerClassVisitorHelper extends ClassCodeVisitorSupport {
 
     protected static int getObjectDistance(ClassNode cn) {
         int count = 0;
-        while (cn != null && cn != ClassHelper.OBJECT_TYPE) {
+        while (cn != null && !cn.equals(ClassHelper.OBJECT_TYPE)) {
             cn = cn.getSuperClass();
             count += 1;
         }
diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
index 9bdd9a6..ddf8fab 100644
--- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -114,6 +114,9 @@ import static org.codehaus.groovy.ast.tools.GenericsUtils.addMethodGenerics;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
 import static org.codehaus.groovy.ast.tools.PropertyNodeUtils.adjustPropertyModifiersForMethod;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
 
 /**
  * Verifies the AST node and adds any default AST code before bytecode generation occurs.
@@ -193,7 +196,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
             return ret;
         }
         ClassNode current = node;
-        while (current != ClassHelper.OBJECT_TYPE) {
+        while (!(current.equals(ClassHelper.OBJECT_TYPE))) {
             current = current.getSuperClass();
             if (current == null) break;
             ret = current.getDeclaredField("metaClass");
@@ -318,7 +321,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
         FieldNode ret = node.getDeclaredField(fieldName);
         if (ret != null) {
             if (isPublic(ret.getModifiers()) &&
-                    ret.getType().redirect() == ClassHelper.boolean_TYPE) {
+                    isPrimitiveBoolean(ret.getType().redirect())) {
                 return ret;
             }
             throw new RuntimeParserException("The class " + node.getName() +
@@ -644,10 +647,10 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
             Parameter[] params = node.getParameters();
             if (params.length == 1) {
                 Parameter param = params[0];
-                if (param.getType() == null || param.getType() == ClassHelper.OBJECT_TYPE) {
+                if (param.getType() == null || param.getType().equals(ClassHelper.OBJECT_TYPE)) {
                     param.setType(ClassHelper.STRING_TYPE.makeArray());
                     ClassNode returnType = node.getReturnType();
-                    if (returnType == ClassHelper.OBJECT_TYPE) {
+                    if (returnType.equals(ClassHelper.OBJECT_TYPE)) {
                         node.setReturnType(ClassHelper.VOID_TYPE);
                     }
                 }
@@ -690,7 +693,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
         Statement getterBlock = node.getGetterBlock();
         if (getterBlock == null) {
             MethodNode getter = classNode.getGetterMethod(getterName, !node.isStatic());
-            if (getter == null && ClassHelper.boolean_TYPE == node.getType()) {
+            if (getter == null && isPrimitiveBoolean(node.getType())) {
                 String secondGetterName = "is" + capitalize(name);
                 getter = classNode.getGetterMethod(secondGetterName);
             }
@@ -715,7 +718,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
         if (getterBlock != null) {
             visitGetter(node, field, getterBlock, getterModifiers, getterName);
 
-            if (node.getGetterName() == null && getterName.startsWith("get") && (node.getType().equals(ClassHelper.boolean_TYPE) || node.getType().equals(ClassHelper.Boolean_TYPE))) {
+            if (node.getGetterName() == null && getterName.startsWith("get") && (isPrimitiveBoolean(node.getType()) || node.getType().equals(ClassHelper.Boolean_TYPE))) {
                 String altGetterName = "is" + capitalize(name);
                 MethodNode altGetter = classNode.getGetterMethod(altGetterName, !node.isStatic());
                 if (methodNeedsReplacement(altGetter)) {
@@ -1453,8 +1456,8 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
                         for (int i = 0, n = para.length; i < n; i += 1) {
                             ClassNode type = para[i].getType();
                             BytecodeHelper.load(mv, type, i + 1 + doubleSlotOffset);
-                            if (type.redirect() == ClassHelper.double_TYPE
-                                    || type.redirect() == ClassHelper.long_TYPE) {
+                            if (isPrimitiveDouble(type.redirect())
+                                    || isPrimitiveLong(type.redirect())) {
                                 doubleSlotOffset += 1;
                             }
                             if (!type.equals(goal[i].getType())) {
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionHelper.java
index 29d8347..e581024 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionHelper.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionHelper.java
@@ -513,7 +513,7 @@ public class BinaryExpressionHelper {
             compareMethod.call(controller.getMethodVisitor());
             ClassNode resType = ClassHelper.boolean_TYPE;
             if (compareMethod == findRegexMethod) {
-                resType = ClassHelper.OBJECT_TYPE;
+                resType = ClassHelper.OBJECT_TYPE.getPlainNodeReference();
             }
             operandStack.replace(resType, 2);
         }
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BinaryObjectExpressionHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryObjectExpressionHelper.java
index d9e6b08..a8d6dbb 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/BinaryObjectExpressionHelper.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryObjectExpressionHelper.java
@@ -92,6 +92,6 @@ public class BinaryObjectExpressionHelper extends BinaryExpressionWriter {
     
     @Override
     protected ClassNode getArrayGetResultType() {
-    	return ClassHelper.OBJECT_TYPE;
+    	return ClassHelper.OBJECT_TYPE.getPlainNodeReference();
     }
 }
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeHelper.java
index 3889a5d..0435716 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeHelper.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeHelper.java
@@ -32,15 +32,16 @@ import org.objectweb.asm.MethodVisitor;
 
 import java.lang.reflect.Modifier;
 
-import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.byte_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.char_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.double_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.float_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveByte;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveChar;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveFloat;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveInt;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveShort;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 import static org.objectweb.asm.Opcodes.ALOAD;
 import static org.objectweb.asm.Opcodes.ARETURN;
 import static org.objectweb.asm.Opcodes.ASTORE;
@@ -209,7 +210,7 @@ public class BytecodeHelper {
      */
     private static String getTypeDescription(ClassNode c, boolean end) {
         ClassNode d = c;
-        if (ClassHelper.isPrimitiveType(d.redirect())) {
+        if (isPrimitiveType(d.redirect())) {
             d = d.redirect();
         }
         String desc = TypeUtil.getDescriptionByType(d);
@@ -414,7 +415,7 @@ public class BytecodeHelper {
         } else {
             ret.append(getTypeDescription(printType, false));
             addSubTypes(ret, printType.getGenericsTypes(), "<", ">");
-            if (!ClassHelper.isPrimitiveType(printType)) ret.append(";");
+            if (!isPrimitiveType(printType)) ret.append(";");
         }
     }
 
@@ -463,8 +464,8 @@ public class BytecodeHelper {
     }
 
     public static void doCast(MethodVisitor mv, ClassNode type) {
-        if (type == ClassHelper.OBJECT_TYPE) return;
-        if (ClassHelper.isPrimitiveType(type) && type != VOID_TYPE) {
+        if (type.equals(ClassHelper.OBJECT_TYPE)) return;
+        if (isPrimitiveType(type) && !isPrimitiveVoid(type)) {
             unbox(mv, type);
         } else {
             mv.visitTypeInsn(
@@ -519,7 +520,7 @@ public class BytecodeHelper {
      */
     @Deprecated
     public static boolean box(MethodVisitor mv, ClassNode type) {
-        if (ClassHelper.isPrimitiveType(type) && !ClassHelper.VOID_TYPE.equals(type)) {
+        if (isPrimitiveType(type) && !isPrimitiveVoid(type)) {
             box(mv, BytecodeHelper.getTypeDescription(type));
             return true;
         }
@@ -546,7 +547,7 @@ public class BytecodeHelper {
      * Generates the bytecode to unbox the current value on the stack.
      */
     public static void unbox(MethodVisitor mv, ClassNode type) {
-        if (ClassHelper.isPrimitiveType(type) && !ClassHelper.VOID_TYPE.equals(type)) {
+        if (isPrimitiveType(type) && !isPrimitiveVoid(type)) {
             unbox(mv, type.getName(), BytecodeHelper.getTypeDescription(type));
         }
     }
@@ -570,7 +571,7 @@ public class BytecodeHelper {
      * If the classnode is not a primitive type, we will generate a LDC instruction.
      */
     public static void visitClassLiteral(MethodVisitor mv, ClassNode classNode) {
-        if (ClassHelper.isPrimitiveType(classNode)) {
+        if (isPrimitiveType(classNode)) {
             mv.visitFieldInsn(
                     GETSTATIC,
                     getClassInternalName(ClassHelper.getWrapper(classNode)),
@@ -633,22 +634,22 @@ public class BytecodeHelper {
      * @param type primitive type to convert
      */
     public static void convertPrimitiveToBoolean(MethodVisitor mv, ClassNode type) {
-        if (type == boolean_TYPE) {
+        if (isPrimitiveBoolean(type)) {
             return;
         }
         // Special handling is done for floating point types in order to
         // handle checking for 0 or NaN values.
-        if (type == double_TYPE) {
+        if (isPrimitiveDouble(type)) {
             convertDoubleToBoolean(mv);
             return;
-        } else if (type == float_TYPE) {
+        } else if (isPrimitiveFloat(type)) {
             convertFloatToBoolean(mv);
             return;
         }
         Label trueLabel = new Label();
         Label falseLabel = new Label();
         // Convert long to int for IFEQ comparison using LCMP
-        if (type == long_TYPE) {
+        if (isPrimitiveLong(type)) {
             mv.visitInsn(LCONST_0);
             mv.visitInsn(LCMP);
         }
@@ -840,20 +841,20 @@ public class BytecodeHelper {
         }
 
         public void handle() {
-            if (type == double_TYPE) {
+            if (isPrimitiveDouble(type)) {
                 handleDoubleType();
-            } else if (type == float_TYPE) {
+            } else if (isPrimitiveFloat(type)) {
                 handleFloatType();
-            } else if (type == long_TYPE) {
+            } else if (isPrimitiveLong(type)) {
                 handleLongType();
             } else if (
-                    type == boolean_TYPE
-                            || type == char_TYPE
-                            || type == byte_TYPE
-                            || type == int_TYPE
-                            || type == short_TYPE) {
+                    isPrimitiveBoolean(type)
+                            || isPrimitiveChar(type)
+                            || isPrimitiveByte(type)
+                            || isPrimitiveInt(type)
+                            || isPrimitiveShort(type)) {
                 handleIntType();
-            } else if (type == VOID_TYPE) {
+            } else if (isPrimitiveVoid(type)) {
                 handleVoidType();
             } else {
                 handleRefType();
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeVariable.java b/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeVariable.java
index 388102f..7bd1865 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeVariable.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeVariable.java
@@ -20,6 +20,7 @@ package org.codehaus.groovy.classgen.asm;
 
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.classgen.asm.util.TypeUtil;
 import org.objectweb.asm.Label;
 
 /**
@@ -105,7 +106,7 @@ public class BytecodeVariable {
 
     public void setType(ClassNode type) {
         this.type = type;
-        dynamicTyped |= type==ClassHelper.DYNAMIC_TYPE;
+        dynamicTyped |= TypeUtil.isDynamicTyped(type);
     }
 
     public void setDynamicTyped(boolean b) {
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
index dda126d..e83494f 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
@@ -177,7 +177,7 @@ public class ClosureWriter {
     }
 
     private static boolean classNodeUsesReferences(final ClassNode classNode) {
-        boolean ret = classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE;
+        boolean ret = classNode.getSuperClass().equals(ClassHelper.CLOSURE_TYPE);
         if (ret) return ret;
         if (classNode instanceof InnerClassNode) {
             InnerClassNode inner = (InnerClassNode) classNode;
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/CompileStack.java b/src/main/java/org/codehaus/groovy/classgen/asm/CompileStack.java
index 5c9bb7f..cd64d6c 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/CompileStack.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/CompileStack.java
@@ -37,6 +37,9 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveFloat;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
 import static org.objectweb.asm.Opcodes.ACONST_NULL;
 import static org.objectweb.asm.Opcodes.ASTORE;
 import static org.objectweb.asm.Opcodes.DCONST_0;
@@ -639,11 +642,11 @@ public class CompileStack {
 
     private static void pushInitValue(final ClassNode type, final MethodVisitor mv) {
         if (ClassHelper.isPrimitiveType(type)) {
-            if (type == ClassHelper.long_TYPE) {
+            if (isPrimitiveLong(type)) {
                 mv.visitInsn(LCONST_0);
-            } else if (type == ClassHelper.double_TYPE) {
+            } else if (isPrimitiveDouble(type)) {
                 mv.visitInsn(DCONST_0);
-            } else if (type == ClassHelper.float_TYPE) {
+            } else if (isPrimitiveFloat(type)) {
                 mv.visitInsn(FCONST_0);
             } else {
                 mv.visitLdcInsn(0);
@@ -676,7 +679,7 @@ public class CompileStack {
         OperandStack operandStack = controller.getOperandStack();
 
         if (!initFromStack) {
-            if (ClassHelper.isPrimitiveType(v.getOriginType()) && ClassHelper.getWrapper(v.getOriginType()) == variableType) {
+            if (ClassHelper.isPrimitiveType(v.getOriginType()) && ClassHelper.getWrapper(v.getOriginType()).equals(variableType)) {
                 pushInitValue(v.getOriginType(), mv);
                 operandStack.push(v.getOriginType());
                 operandStack.box();
@@ -712,7 +715,7 @@ public class CompileStack {
      */
     private void makeNextVariableID(final ClassNode type, final boolean useReferenceDirectly) {
         currentVariableIndex = nextVariableIndex;
-        if ((type == ClassHelper.long_TYPE || type == ClassHelper.double_TYPE) && !useReferenceDirectly) {
+        if ((isPrimitiveLong(type) || isPrimitiveDouble(type)) && !useReferenceDirectly) {
             nextVariableIndex += 1;
         }
         nextVariableIndex += 1;
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
index ac9e639..497cfbf 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
@@ -57,6 +57,7 @@ import static org.apache.groovy.ast.tools.ExpressionUtils.isSuperExpression;
 import static org.apache.groovy.ast.tools.ExpressionUtils.isThisExpression;
 import static org.codehaus.groovy.ast.ClassHelper.isFunctionalInterface;
 import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isClassClassNodeWrappingConcreteType;
 import static org.objectweb.asm.Opcodes.AALOAD;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
@@ -205,7 +206,7 @@ public class InvocationWriter {
         String descriptor = BytecodeHelper.getMethodDescriptor(target.getReturnType(), target.getParameters());
         mv.visitMethodInsn(opcode, owner, methodName, descriptor, declaringClass.isInterface());
         ClassNode returnType = target.getReturnType().redirect();
-        if (returnType == ClassHelper.VOID_TYPE) {
+        if (isPrimitiveVoid(returnType)) {
             returnType = ClassHelper.OBJECT_TYPE;
             mv.visitInsn(ACONST_NULL);
         }
@@ -429,7 +430,7 @@ public class InvocationWriter {
         String methodName = null;
         if (message instanceof CastExpression) {
             CastExpression msg = (CastExpression) message;
-            if (msg.getType() == ClassHelper.STRING_TYPE) {
+            if (msg.getType().equals(ClassHelper.STRING_TYPE)) {
                 final Expression methodExpr = msg.getExpression();
                 if (methodExpr instanceof ConstantExpression) {
                     methodName = methodExpr.getText();
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/MopWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/MopWriter.java
index 01086a3..c926995 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/MopWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/MopWriter.java
@@ -35,6 +35,8 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
 import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
 import static org.objectweb.asm.Opcodes.ACC_BRIDGE;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
@@ -191,7 +193,7 @@ public class MopWriter {
                 ClassNode type = parameter.getType();
                 operandStack.load(parameter.getType(), newRegister);
                 newRegister += 1; // increment to next register; double/long are using two places
-                if (type == ClassHelper.double_TYPE || type == ClassHelper.long_TYPE) newRegister += 1;
+                if (isPrimitiveDouble(type) || isPrimitiveLong(type)) newRegister += 1;
             }
             operandStack.remove(parameters.length);
             ClassNode declaringClass = method.getDeclaringClass();
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/OperandStack.java b/src/main/java/org/codehaus/groovy/classgen/asm/OperandStack.java
index 23476c4..b628062 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/OperandStack.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/OperandStack.java
@@ -38,6 +38,15 @@ import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.List;
 
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveByte;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveChar;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveFloat;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveInt;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveShort;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 import static org.objectweb.asm.Opcodes.ACONST_NULL;
 import static org.objectweb.asm.Opcodes.ALOAD;
 import static org.objectweb.asm.Opcodes.BIPUSH;
@@ -121,7 +130,7 @@ public class OperandStack {
      * returns true for long and double
      */
     private static boolean isTwoSlotType(final ClassNode type) {
-        return type == ClassHelper.long_TYPE || type == ClassHelper.double_TYPE;
+        return isPrimitiveLong(type) || isPrimitiveDouble(type);
     }
 
     /**
@@ -146,7 +155,7 @@ public class OperandStack {
         } else if (mark == size - 1) {
             ClassNode last = stack.get(size - 1);
             // nothing to do in that case
-            if (last == ClassHelper.boolean_TYPE) return;
+            if (isPrimitiveBoolean(last)) return;
             if (ClassHelper.isPrimitiveType(last)) {
                 BytecodeHelper.convertPrimitiveToBoolean(mv, last);
             } else {
@@ -195,7 +204,7 @@ public class OperandStack {
         MethodVisitor mv = controller.getMethodVisitor();
         int size = stack.size();
         ClassNode type = stack.get(size - 1);
-        if (ClassHelper.isPrimitiveType(type) && ClassHelper.VOID_TYPE != type) {
+        if (ClassHelper.isPrimitiveType(type) && !isPrimitiveVoid(type)) {
             ClassNode wrapper = ClassHelper.getWrapper(type);
             BytecodeHelper.doCastToWrappedType(mv, type, wrapper);
             type = wrapper;
@@ -360,7 +369,7 @@ public class OperandStack {
         }
 
         MethodVisitor mv = controller.getMethodVisitor();
-        if (primTarget && !ClassHelper.boolean_TYPE.equals(targetType)
+        if (primTarget && !isPrimitiveBoolean(targetType)
                 && !primTop && ClassHelper.getWrapper(targetType).equals(top)) {
             BytecodeHelper.doCastToPrimitive(mv, top, targetType);
         } else {
@@ -374,17 +383,17 @@ public class OperandStack {
 
     private boolean convertFromInt(final ClassNode target) {
         int convertCode;
-        if (target == ClassHelper.char_TYPE) {
+        if (isPrimitiveChar(target)) {
             convertCode = I2C;
-        } else if (target == ClassHelper.byte_TYPE) {
+        } else if (isPrimitiveByte(target)) {
             convertCode = I2B;
-        } else if (target == ClassHelper.short_TYPE) {
+        } else if (isPrimitiveShort(target)) {
             convertCode = I2S;
-        } else if (target == ClassHelper.long_TYPE) {
+        } else if (isPrimitiveLong(target)) {
             convertCode = I2L;
-        } else if (target == ClassHelper.float_TYPE) {
+        } else if (isPrimitiveFloat(target)) {
             convertCode = I2F;
-        } else if (target == ClassHelper.double_TYPE) {
+        } else if (isPrimitiveDouble(target)) {
             convertCode = I2D;
         } else {
             return false;
@@ -395,18 +404,18 @@ public class OperandStack {
 
     private boolean convertFromLong(final ClassNode target) {
         MethodVisitor mv = controller.getMethodVisitor();
-        if (target == ClassHelper.int_TYPE) {
+        if (isPrimitiveInt(target)) {
             mv.visitInsn(L2I);
             return true;
-        } else if (target == ClassHelper.char_TYPE
-                || target == ClassHelper.byte_TYPE
-                || target == ClassHelper.short_TYPE) {
+        } else if (isPrimitiveChar(target)
+                || isPrimitiveByte(target)
+                || isPrimitiveShort(target)) {
             mv.visitInsn(L2I);
             return convertFromInt(target);
-        } else if (target == ClassHelper.double_TYPE) {
+        } else if (isPrimitiveDouble(target)) {
             mv.visitInsn(L2D);
             return true;
-        } else if (target == ClassHelper.float_TYPE) {
+        } else if (isPrimitiveFloat(target)) {
             mv.visitInsn(L2F);
             return true;
         }
@@ -415,18 +424,18 @@ public class OperandStack {
 
     private boolean convertFromDouble(final ClassNode target) {
         MethodVisitor mv = controller.getMethodVisitor();
-        if (target == ClassHelper.int_TYPE) {
+        if (isPrimitiveInt(target)) {
             mv.visitInsn(D2I);
             return true;
-        } else if (target == ClassHelper.char_TYPE
-                || target == ClassHelper.byte_TYPE
-                || target == ClassHelper.short_TYPE) {
+        } else if (isPrimitiveChar(target)
+                || isPrimitiveByte(target)
+                || isPrimitiveShort(target)) {
             mv.visitInsn(D2I);
             return convertFromInt(target);
-        } else if (target == ClassHelper.long_TYPE) {
+        } else if (isPrimitiveLong(target)) {
             mv.visitInsn(D2L);
             return true;
-        } else if (target == ClassHelper.float_TYPE) {
+        } else if (isPrimitiveFloat(target)) {
             mv.visitInsn(D2F);
             return true;
         }
@@ -435,18 +444,18 @@ public class OperandStack {
 
     private boolean convertFromFloat(final ClassNode target) {
         MethodVisitor mv = controller.getMethodVisitor();
-        if (target == ClassHelper.int_TYPE) {
+        if (isPrimitiveInt(target)) {
             mv.visitInsn(F2I);
             return true;
-        } else if (target == ClassHelper.char_TYPE
-                || target == ClassHelper.byte_TYPE
-                || target == ClassHelper.short_TYPE) {
+        } else if (isPrimitiveChar(target)
+                || isPrimitiveByte(target)
+                || isPrimitiveShort(target)) {
             mv.visitInsn(F2I);
             return convertFromInt(target);
-        } else if (target == ClassHelper.long_TYPE) {
+        } else if (isPrimitiveLong(target)) {
             mv.visitInsn(F2L);
             return true;
-        } else if (target == ClassHelper.double_TYPE) {
+        } else if (isPrimitiveDouble(target)) {
             mv.visitInsn(F2D);
             return true;
         }
@@ -456,17 +465,17 @@ public class OperandStack {
     private boolean convertPrimitive(final ClassNode top, final ClassNode target) {
         if (top == target)
             return true;
-        if (top == ClassHelper.int_TYPE) {
+        if (isPrimitiveInt(top)) {
             return convertFromInt(target);
-        } else if (top == ClassHelper.char_TYPE
-                || top == ClassHelper.byte_TYPE
-                || top == ClassHelper.short_TYPE) {
-            return target == ClassHelper.int_TYPE || convertFromInt(target);
-        } else if (top == ClassHelper.float_TYPE) {
+        } else if (isPrimitiveChar(top)
+                || isPrimitiveByte(top)
+                || isPrimitiveShort(top)) {
+            return isPrimitiveInt(target) || convertFromInt(target);
+        } else if (isPrimitiveFloat(top)) {
             return convertFromFloat(target);
-        } else if (top == ClassHelper.double_TYPE) {
+        } else if (isPrimitiveDouble(top)) {
             return convertFromDouble(target);
-        } else if (top == ClassHelper.long_TYPE) {
+        } else if (isPrimitiveLong(top)) {
             return convertFromLong(target);
         }
         return false;
@@ -480,7 +489,7 @@ public class OperandStack {
         Object value = expression.getValue();
         ClassNode origType = expression.getType().redirect();
         ClassNode type = ClassHelper.getUnwrapper(origType);
-        boolean boxing = origType != type;
+        boolean boxing = !origType.equals(type);
         boolean asPrimitive = boxing || ClassHelper.isPrimitiveType(type);
 
         if (value == null) {
@@ -518,14 +527,14 @@ public class OperandStack {
     }
 
     private static void pushPrimitiveConstant(final MethodVisitor mv, final Object value, final ClassNode type) {
-        boolean isInt = ClassHelper.int_TYPE.equals(type);
-        boolean isShort = ClassHelper.short_TYPE.equals(type);
-        boolean isByte = ClassHelper.byte_TYPE.equals(type);
-        boolean isChar = ClassHelper.char_TYPE.equals(type);
+        boolean isInt = isPrimitiveInt(type);
+        boolean isShort = isPrimitiveShort(type);
+        boolean isByte = isPrimitiveByte(type);
+        boolean isChar = isPrimitiveChar(type);
         if (isInt || isShort || isByte || isChar) {
             int val = isInt ? (Integer) value : isShort ? (Short) value : isChar ? (Character) value : (Byte) value;
             BytecodeHelper.pushConstant(mv, val);
-        } else if (ClassHelper.long_TYPE.equals(type)) {
+        } else if (isPrimitiveLong(type)) {
             if ((Long) value == 0L) {
                 mv.visitInsn(LCONST_0);
             } else if ((Long) value == 1L) {
@@ -533,7 +542,7 @@ public class OperandStack {
             } else {
                 mv.visitLdcInsn(value);
             }
-        } else if (ClassHelper.float_TYPE.equals(type)) {
+        } else if (isPrimitiveFloat(type)) {
             // GROOVY-9797: Use Float.equals to differentiate between positive and negative zero
             if (value.equals(0f)) {
                 mv.visitInsn(FCONST_0);
@@ -544,7 +553,7 @@ public class OperandStack {
             } else {
                 mv.visitLdcInsn(value);
             }
-        } else if (ClassHelper.double_TYPE.equals(type)) {
+        } else if (isPrimitiveDouble(type)) {
             // GROOVY-9797: Use Double.equals to differentiate between positive and negative zero
             if (value.equals(0d)) {
                 mv.visitInsn(DCONST_0);
@@ -553,7 +562,7 @@ public class OperandStack {
             } else {
                 mv.visitLdcInsn(value);
             }
-        } else if (ClassHelper.boolean_TYPE.equals(type)) {
+        } else if (isPrimitiveBoolean(type)) {
             boolean b = (Boolean) value;
             if (b) {
                 mv.visitInsn(ICONST_1);
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/OptimizingStatementWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/OptimizingStatementWriter.java
index afbf205..59c9087 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/OptimizingStatementWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/OptimizingStatementWriter.java
@@ -80,6 +80,7 @@ import static org.codehaus.groovy.ast.tools.WideningCategories.isIntCategory;
 import static org.codehaus.groovy.ast.tools.WideningCategories.isLongCategory;
 import static org.codehaus.groovy.classgen.asm.BinaryExpressionMultiTypeDispatcher.typeMap;
 import static org.codehaus.groovy.classgen.asm.BinaryExpressionMultiTypeDispatcher.typeMapKeyNames;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
 import static org.objectweb.asm.Opcodes.GETSTATIC;
 import static org.objectweb.asm.Opcodes.GOTO;
@@ -706,7 +707,7 @@ public class OptimizingStatementWriter extends StatementWriter {
                     case Types.LOGICAL_AND_EQUAL:
                     case Types.LOGICAL_OR:
                     case Types.LOGICAL_OR_EQUAL:
-                        if (boolean_TYPE.equals(leftType) && boolean_TYPE.equals(rightType)) {
+                        if (isPrimitiveBoolean(leftType) && isPrimitiveBoolean(rightType)) {
                             opt.chainShouldOptimize(true);
                         } else {
                             opt.chainCanOptimize(true);
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java
index daeefcd..347f807 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java
@@ -53,6 +53,7 @@ import java.util.List;
 import java.util.Optional;
 import java.util.function.Consumer;
 
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 import static org.objectweb.asm.Opcodes.ALOAD;
 import static org.objectweb.asm.Opcodes.ATHROW;
 import static org.objectweb.asm.Opcodes.CHECKCAST;
@@ -567,7 +568,7 @@ public class StatementWriter {
         OperandStack operandStack = controller.getOperandStack();
         ClassNode returnType = controller.getReturnType();
 
-        if (returnType.equals(ClassHelper.VOID_TYPE)) {
+        if (isPrimitiveVoid(returnType)) {
             if (!statement.isReturningNullOrVoid()) { // TODO: move to Verifier
                 controller.getAcg().throwException("Cannot use return statement with an expression on a method that returns void");
             }
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/indy/InvokeDynamicWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/indy/InvokeDynamicWriter.java
index 000dac6..d2735ac 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/indy/InvokeDynamicWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/indy/InvokeDynamicWriter.java
@@ -44,6 +44,7 @@ import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.invoke.MethodType;
 
 import static org.codehaus.groovy.classgen.asm.BytecodeHelper.getTypeDescription;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
 import static org.codehaus.groovy.vmplugin.v8.IndyInterface.CallType.CAST;
 import static org.codehaus.groovy.vmplugin.v8.IndyInterface.CallType.GET;
 import static org.codehaus.groovy.vmplugin.v8.IndyInterface.CallType.INIT;
@@ -194,7 +195,7 @@ public class InvokeDynamicWriter extends InvocationWriter {
     public void coerce(ClassNode from, ClassNode target) {
         ClassNode wrapper = ClassHelper.getWrapper(target);
         makeIndyCall(invokeMethod, EmptyExpression.INSTANCE, false, false, "asType", new ClassExpression(wrapper));
-        if (ClassHelper.boolean_TYPE.equals(target) || ClassHelper.Boolean_TYPE.equals(target)) {
+        if (isPrimitiveBoolean(target) || ClassHelper.Boolean_TYPE.equals(target)) {
             writeIndyCast(ClassHelper.OBJECT_TYPE,target);
         } else {
             BytecodeHelper.doCast(controller.getMethodVisitor(), wrapper);
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
index 4274ba1..a6729a8 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
@@ -24,6 +24,7 @@ import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.classgen.asm.BytecodeHelper;
+import org.codehaus.groovy.classgen.asm.util.TypeUtil;
 import org.codehaus.groovy.syntax.RuntimeParserException;
 import org.objectweb.asm.Handle;
 import org.objectweb.asm.Opcodes;
@@ -116,8 +117,8 @@ public interface AbstractFunctionalInterfaceWriter {
         boolean isParameterTypePrimitive = ClassHelper.isPrimitiveType(parameterType);
         boolean isInferredTypePrimitive = ClassHelper.isPrimitiveType(inferredType);
         if (!isParameterTypePrimitive && isInferredTypePrimitive) {
-            if (ClassHelper.DYNAMIC_TYPE.equals(parameterType) && ClassHelper.isPrimitiveType(targetType) // (1)
-                    || parameterType != getUnwrapper(parameterType) && inferredType != getWrapper(inferredType) // (2)
+            if (TypeUtil.isDynamicTyped(parameterType) && ClassHelper.isPrimitiveType(targetType) // (1)
+                    || !parameterType.equals(getUnwrapper(parameterType)) && !inferredType.equals(getWrapper(inferredType)) // (2)
             ) {
                 // GROOVY-9790: bootstrap method initialization exception raised when lambda parameter type is wrong
                 // (1) java.lang.invoke.LambdaConversionException: Type mismatch for instantiated parameter 0: class java.lang.Integer is not a subtype of int
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
index 6d0b8f4..4b770da 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
@@ -84,6 +84,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 import static org.codehaus.groovy.transform.trait.Traits.isTrait;
 import static org.objectweb.asm.Opcodes.ACONST_NULL;
 import static org.objectweb.asm.Opcodes.ALOAD;
@@ -312,7 +313,7 @@ public class StaticInvocationWriter extends InvocationWriter {
             mv.visitMethodInsn(INVOKESTATIC, owner, target.getName(), desc, false);
             controller.getOperandStack().remove(argumentList.size());
 
-            if (ClassHelper.VOID_TYPE.equals(returnType)) {
+            if (isPrimitiveVoid(returnType)) {
                 returnType = ClassHelper.OBJECT_TYPE;
                 mv.visitInsn(ACONST_NULL);
             }
@@ -629,7 +630,7 @@ public class StaticInvocationWriter extends InvocationWriter {
             newMCE.visit(controller.getAcg());
             compileStack.removeVar(slot.getIndex());
             ClassNode returnType = operandStack.getTopOperand();
-            if (ClassHelper.isPrimitiveType(returnType) && !ClassHelper.VOID_TYPE.equals(returnType)) {
+            if (ClassHelper.isPrimitiveType(returnType) && !isPrimitiveVoid(returnType)) {
                 operandStack.box();
             }
             Label endof = compileStack.createLocalLabel("endof_" + counter);
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
index 9ad87e9..7983f76 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
@@ -55,10 +55,6 @@ import java.util.concurrent.atomic.AtomicInteger;
 
 import static org.apache.groovy.ast.tools.ExpressionUtils.isThisExpression;
 import static org.codehaus.groovy.ast.ClassHelper.CLOSURE_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.char_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.double_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.float_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.binX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
@@ -72,6 +68,10 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveChar;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveFloat;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
 import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.PRIVATE_FIELDS_MUTATORS;
 import static org.codehaus.groovy.transform.sc.StaticCompilationVisitor.ARRAYLIST_ADD_METHOD;
 import static org.codehaus.groovy.transform.sc.StaticCompilationVisitor.ARRAYLIST_CLASSNODE;
@@ -124,7 +124,7 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher extends BinaryExpres
         }
 
         ClassNode top = controller.getOperandStack().getTopOperand();
-        if (ClassHelper.isPrimitiveType(top) && (ClassHelper.isNumberType(top) || char_TYPE.equals(top))) {
+        if (ClassHelper.isPrimitiveType(top) && (ClassHelper.isNumberType(top) || isPrimitiveChar(top))) {
             MethodVisitor mv = controller.getMethodVisitor();
             visitInsnByType(top, mv, ICONST_1, LCONST_1, FCONST_1, DCONST_1);
             if ("next".equals(method)) {
@@ -139,13 +139,13 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher extends BinaryExpres
     }
 
     private static void visitInsnByType(final ClassNode top, final MethodVisitor mv, final int iInsn, final int lInsn, final int fInsn, final int dInsn) {
-        if (WideningCategories.isIntCategory(top) || char_TYPE.equals(top)) {
+        if (WideningCategories.isIntCategory(top) || isPrimitiveChar(top)) {
             mv.visitInsn(iInsn);
-        } else if (long_TYPE.equals(top)) {
+        } else if (isPrimitiveLong(top)) {
             mv.visitInsn(lInsn);
-        } else if (float_TYPE.equals(top)) {
+        } else if (isPrimitiveFloat(top)) {
             mv.visitInsn(fInsn);
-        } else if (double_TYPE.equals(top)) {
+        } else if (isPrimitiveDouble(top)) {
             mv.visitInsn(dInsn);
         }
     }
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
index dea5672..0bfcde8 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
@@ -72,7 +72,6 @@ import static org.codehaus.groovy.ast.ClassHelper.MAP_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.Number_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.STRING_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.getUnwrapper;
 import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
 import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
@@ -89,6 +88,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.isOrImplements;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.chooseBestMethod;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf;
@@ -482,7 +482,7 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter {
         if (getterNode == null && propertyNode != null) {
             // it is possible to use a getter
             String prefix = "get";
-            if (boolean_TYPE.equals(propertyNode.getOriginType())) {
+            if (isPrimitiveBoolean(propertyNode.getOriginType())) {
                 prefix = "is";
             }
             getterName = prefix + capitalize(propertyName);
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesStatementWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesStatementWriter.java
index cac69f7..0ab8c6e 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesStatementWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesStatementWriter.java
@@ -39,6 +39,15 @@ import org.objectweb.asm.MethodVisitor;
 
 import java.util.Enumeration;
 
+import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveByte;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveChar;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveFloat;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveInt;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveShort;
 import static org.objectweb.asm.Opcodes.AALOAD;
 import static org.objectweb.asm.Opcodes.ALOAD;
 import static org.objectweb.asm.Opcodes.ARRAYLENGTH;
@@ -169,33 +178,21 @@ public class StaticTypesStatementWriter extends StatementWriter {
         mv.visitVarInsn(ILOAD, iteratorIdx);
 
         ClassNode varType = variable.getType();
-        boolean primitiveType = ClassHelper.isPrimitiveType(varType);
-        boolean isByte = ClassHelper.byte_TYPE.equals(varType);
-        boolean isShort = ClassHelper.short_TYPE.equals(varType);
-        boolean isInt = ClassHelper.int_TYPE.equals(varType);
-        boolean isLong = ClassHelper.long_TYPE.equals(varType);
-        boolean isFloat = ClassHelper.float_TYPE.equals(varType);
-        boolean isDouble = ClassHelper.double_TYPE.equals(varType);
-        boolean isChar = ClassHelper.char_TYPE.equals(varType);
-        boolean isBoolean = ClassHelper.boolean_TYPE.equals(varType);
-
-        if (primitiveType) {
-            if (isByte) {
+
+        if (isPrimitiveType(varType)) {
+            if (isPrimitiveInt(varType)) {
+                mv.visitInsn(IALOAD);
+            } else if (isPrimitiveLong(varType)) {
+                mv.visitInsn(LALOAD);
+            } else if (isPrimitiveByte(varType) || isPrimitiveBoolean(varType)) {
                 mv.visitInsn(BALOAD);
-            }
-            if (isShort) {
+            } else if (isPrimitiveChar(varType)) {
+                mv.visitInsn(CALOAD);
+            } else if (isPrimitiveShort(varType)) {
                 mv.visitInsn(SALOAD);
-            }
-            if (isInt || isChar || isBoolean) {
-                mv.visitInsn(isChar ? CALOAD : isBoolean ? BALOAD : IALOAD);
-            }
-            if (isLong) {
-                mv.visitInsn(LALOAD);
-            }
-            if (isFloat) {
+            } else if (isPrimitiveFloat(varType)) {
                 mv.visitInsn(FALOAD);
-            }
-            if (isDouble) {
+            } else if (isPrimitiveDouble(varType)) {
                 mv.visitInsn(DALOAD);
             }
         } else {
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesTypeChooser.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesTypeChooser.java
index 554a8cd..a5dd377 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesTypeChooser.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesTypeChooser.java
@@ -19,13 +19,14 @@
 package org.codehaus.groovy.classgen.asm.sc;
 
 import org.codehaus.groovy.ast.ASTNode;
-import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.VariableExpression;
 import org.codehaus.groovy.classgen.asm.StatementMetaTypeChooser;
 import org.codehaus.groovy.transform.stc.StaticTypesMarker;
 
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
+
 /**
  * A {@link org.codehaus.groovy.classgen.asm.TypeChooser TypeChooser} which reads
  * type information from node metadata generated by the static type checker.
@@ -39,7 +40,7 @@ public class StaticTypesTypeChooser extends StatementMetaTypeChooser {
         if (inferredType == null) {
             inferredType = target.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
         }
-        if (inferredType != null && !ClassHelper.VOID_TYPE.equals(inferredType)) {
+        if (inferredType != null && !isPrimitiveVoid(inferredType)) {
             return inferredType;
         }
 
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesUnaryExpressionHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesUnaryExpressionHelper.java
index fadcc56..b60ec0b 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesUnaryExpressionHelper.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesUnaryExpressionHelper.java
@@ -31,14 +31,15 @@ import org.codehaus.groovy.classgen.asm.WriterController;
 import org.objectweb.asm.Label;
 
 import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.byte_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.char_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.double_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.float_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.bytecodeX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveByte;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveChar;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveFloat;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveInt;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveShort;
 import static org.objectweb.asm.Opcodes.DNEG;
 import static org.objectweb.asm.Opcodes.FNEG;
 import static org.objectweb.asm.Opcodes.GOTO;
@@ -72,19 +73,19 @@ public class StaticTypesUnaryExpressionHelper extends UnaryExpressionHelper {
     public void writeBitwiseNegate(final BitwiseNegationExpression expression) {
         expression.getExpression().visit(controller.getAcg());
         ClassNode top = controller.getOperandStack().getTopOperand();
-        if (top == int_TYPE || top == long_TYPE || top == short_TYPE || top == byte_TYPE || top == char_TYPE) {
+        if (isPrimitiveInt(top) || isPrimitiveLong(top) || isPrimitiveShort(top) || isPrimitiveByte(top) || isPrimitiveChar(top)) {
             bytecodeX(mv -> {
-                if (top == long_TYPE) {
+                if (isPrimitiveLong(top)) {
                     mv.visitLdcInsn(-1L);
                     mv.visitInsn(LXOR);
                 } else {
                     mv.visitInsn(ICONST_M1);
                     mv.visitInsn(IXOR);
-                    if (top == byte_TYPE) {
+                    if (isPrimitiveByte(top)) {
                         mv.visitInsn(I2B);
-                    } else if (top == char_TYPE) {
+                    } else if (isPrimitiveChar(top)) {
                         mv.visitInsn(I2C);
-                    } else if (top == short_TYPE) {
+                    } else if (isPrimitiveShort(top)) {
                         mv.visitInsn(I2S);
                     }
                 }
@@ -99,7 +100,7 @@ public class StaticTypesUnaryExpressionHelper extends UnaryExpressionHelper {
     public void writeNotExpression(final NotExpression expression) {
         Expression subExpression = expression.getExpression();
         TypeChooser typeChooser = controller.getTypeChooser();
-        if (typeChooser.resolveType(subExpression, controller.getClassNode()) == boolean_TYPE) {
+        if (isPrimitiveBoolean(typeChooser.resolveType(subExpression, controller.getClassNode()))) {
             subExpression.visit(controller.getAcg());
             controller.getOperandStack().doGroovyCast(boolean_TYPE);
             bytecodeX(mv -> {
@@ -122,22 +123,23 @@ public class StaticTypesUnaryExpressionHelper extends UnaryExpressionHelper {
     public void writeUnaryMinus(final UnaryMinusExpression expression) {
         expression.getExpression().visit(controller.getAcg());
         ClassNode top = controller.getOperandStack().getTopOperand();
-        if (top == int_TYPE || top == long_TYPE || top == short_TYPE || top == float_TYPE || top == double_TYPE || top == byte_TYPE || top == char_TYPE) {
+        if (isPrimitiveInt(top) || isPrimitiveLong(top) || isPrimitiveShort(top)|| isPrimitiveFloat(top)
+                || isPrimitiveDouble(top) || isPrimitiveByte(top) || isPrimitiveChar(top)) {
             bytecodeX(mv -> {
-                if (top == int_TYPE || top == short_TYPE || top == byte_TYPE || top == char_TYPE) {
+                if (isPrimitiveInt(top) || isPrimitiveShort(top) || isPrimitiveByte(top) || isPrimitiveChar(top)) {
                     mv.visitInsn(INEG);
-                    if (top == byte_TYPE) {
+                    if (isPrimitiveByte(top)) {
                         mv.visitInsn(I2B);
-                    } else if (top == char_TYPE) {
+                    } else if (isPrimitiveChar(top)) {
                         mv.visitInsn(I2C);
-                    } else if (top == short_TYPE) {
+                    } else if (isPrimitiveShort(top)) {
                         mv.visitInsn(I2S);
                     }
-                } else if (top == long_TYPE) {
+                } else if (isPrimitiveLong(top)) {
                     mv.visitInsn(LNEG);
-                } else if (top == float_TYPE) {
+                } else if (isPrimitiveFloat(top)) {
                     mv.visitInsn(FNEG);
-                } else if (top == double_TYPE) {
+                } else if (isPrimitiveDouble(top)) {
                     mv.visitInsn(DNEG);
                 }
             }).visit(controller.getAcg());
@@ -151,7 +153,8 @@ public class StaticTypesUnaryExpressionHelper extends UnaryExpressionHelper {
     public void writeUnaryPlus(final UnaryPlusExpression expression) {
         expression.getExpression().visit(controller.getAcg());
         ClassNode top = controller.getOperandStack().getTopOperand();
-        if (top == int_TYPE || top == long_TYPE || top == short_TYPE || top == float_TYPE || top == double_TYPE || top == byte_TYPE || top == char_TYPE) {
+        if (isPrimitiveInt(top) || isPrimitiveLong(top) || isPrimitiveShort(top)|| isPrimitiveFloat(top)
+                || isPrimitiveDouble(top) || isPrimitiveByte(top) || isPrimitiveChar(top)) {
             // only visit the sub-expression
         } else {
             super.writeUnaryPlus(EMPTY_UNARY_PLUS);
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/util/TypeUtil.java b/src/main/java/org/codehaus/groovy/classgen/asm/util/TypeUtil.java
index e3d1ade..b7f0b86 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/util/TypeUtil.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/util/TypeUtil.java
@@ -26,6 +26,7 @@ import org.objectweb.asm.Type;
 
 import java.util.Map;
 
+import static org.codehaus.groovy.ast.ClassHelper.DYNAMIC_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.byte_TYPE;
@@ -96,7 +97,47 @@ public abstract class TypeUtil {
     }
 
     public static boolean isPrimitiveType(ClassNode type) {
-        return PRIMITIVE_TYPE_TO_DESCRIPTION_MAP.containsKey(type);
+        return PRIMITIVE_TYPE_TO_DESCRIPTION_MAP.containsKey(type.redirect());
+    }
+
+    public static boolean isDynamicTyped(ClassNode type) {
+        return type != null && DYNAMIC_TYPE == type.redirect();
+    }
+
+    public static boolean isPrimitiveBoolean(ClassNode type) {
+        return type.redirect() == boolean_TYPE;
+    }
+
+    public static boolean isPrimitiveChar(ClassNode type) {
+        return type.redirect() == char_TYPE;
+    }
+
+    public static boolean isPrimitiveByte(ClassNode type) {
+        return type.redirect() == byte_TYPE;
+    }
+
+    public static boolean isPrimitiveInt(ClassNode type) {
+        return type.redirect() == int_TYPE;
+    }
+
+    public static boolean isPrimitiveLong(ClassNode type) {
+        return type.redirect() == long_TYPE;
+    }
+
+    public static boolean isPrimitiveShort(ClassNode type) {
+        return type.redirect() == short_TYPE;
+    }
+
+    public static boolean isPrimitiveDouble(ClassNode type) {
+        return type.redirect() == double_TYPE;
+    }
+
+    public static boolean isPrimitiveFloat(ClassNode type) {
+        return type.redirect() == float_TYPE;
+    }
+
+    public static boolean isPrimitiveVoid(ClassNode type) {
+        return type.redirect() == VOID_TYPE;
     }
 
     public static String getDescriptionByType(ClassNode type) {
diff --git a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
index 7dcdd5c..1cc1bf7 100644
--- a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
@@ -62,6 +62,7 @@ import org.codehaus.groovy.ast.stmt.BlockStatement;
 import org.codehaus.groovy.ast.stmt.CatchStatement;
 import org.codehaus.groovy.ast.stmt.ForStatement;
 import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.classgen.asm.util.TypeUtil;
 import org.codehaus.groovy.control.ClassNodeResolver.LookupResult;
 import org.codehaus.groovy.runtime.memoize.UnlimitedConcurrentCache;
 import org.codehaus.groovy.syntax.Types;
@@ -263,6 +264,13 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
     }
 
     @Override
+    public void visitMethod(MethodNode node) {
+        super.visitMethod(node);
+        visitGenericsTypeAnnotations(node);
+        visitTypeAnnotations(node.getReturnType());
+    }
+
+    @Override
     protected void visitConstructorOrMethod(final MethodNode node, final boolean isConstructor) {
         VariableScope oldScope = currentScope;
         currentScope = node.getVariableScope();
@@ -278,11 +286,12 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
             resolveOrFail(p.getType(), p.getType());
             visitAnnotations(p);
         }
-        ClassNode[] exceptions = node.getExceptions();
-        for (ClassNode t : exceptions) {
-            resolveOrFail(t, node);
-        }
         resolveOrFail(node.getReturnType(), node);
+        if (node.getExceptions() != null) {
+            for (ClassNode t : node.getExceptions()) {
+                resolveOrFail(t, node);
+            }
+        }
 
         MethodNode oldCurrentMethod = currentMethod;
         currentMethod = node;
@@ -326,6 +335,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
     }
 
     private void resolveOrFail(final ClassNode type, final String msg, final ASTNode node, final boolean preferImports) {
+        visitTypeAnnotations(type);
         if (preferImports) {
             resolveGenericsTypes(type.getGenericsTypes());
             if (resolveAliasFromModule(type)) return;
@@ -509,7 +519,8 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
     protected boolean resolveNestedClass(final ClassNode type) {
         if (type instanceof ConstructedNestedClass || type instanceof ConstructedClassWithPackage) return false;
 
-        ClassNode cn = currentClass; Set<ClassNode> cycleCheck = new HashSet<>();
+        ClassNode cn = currentClass;
+        Set<ClassNode> cycleCheck = new HashSet<>();
         // GROOVY-4043: for type "X", try "A$X" with each type in the class hierarchy (except for Object)
         for (; cn != null && cycleCheck.add(cn) && !cn.equals(ClassHelper.OBJECT_TYPE); cn = cn.getSuperClass()) {
             if (setRedirect(type, cn)) return true;
@@ -1324,8 +1335,36 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
 
     @Override
     public void visitAnnotations(final AnnotatedNode node) {
-        List<AnnotationNode> annotations = node.getAnnotations();
-        if (annotations.isEmpty()) return;
+        visitAnnotations(node.getAnnotations());
+    }
+
+    private void visitTypeAnnotations(final ClassNode node) {
+        visitAnnotations(node.getTypeAnnotations());
+        visitGenericsTypeAnnotations(node);
+    }
+
+    private void visitGenericsTypeAnnotations(final ClassNode node) {
+        GenericsType[] genericsTypes = node.getGenericsTypes();
+        if (node.isUsingGenerics() && genericsTypes != null) {
+            visitGenericsTypeAnnotations(genericsTypes);
+        }
+    }
+
+    private void visitGenericsTypeAnnotations(final MethodNode node) {
+        GenericsType[] genericsTypes = node.getGenericsTypes();
+        if (genericsTypes != null) {
+            visitGenericsTypeAnnotations(genericsTypes);
+        }
+    }
+
+    private void visitGenericsTypeAnnotations(final GenericsType[] genericsTypes) {
+        for (GenericsType gt : genericsTypes) {
+            visitTypeAnnotations(gt.getType());
+        }
+    }
+
+    private void visitAnnotations(List<AnnotationNode> annotations) {
+        if (annotations == null || annotations.isEmpty()) return;
         for (AnnotationNode an : annotations) {
             // skip built-in properties
             if (an.isBuiltIn()) continue;
@@ -1394,6 +1433,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
             genericParameterNames = new HashMap<>();
         }
 
+        visitTypeAnnotations(node);
         resolveGenericsHeader(node.getGenericsTypes());
 
         ModuleNode module = node.getModule();
@@ -1495,7 +1535,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
                     }
                 }
             }
-            if (parentToCompare == ClassHelper.OBJECT_TYPE) return;
+            if (parentToCompare.equals(ClassHelper.OBJECT_TYPE)) return;
             checkCyclicInheritance(originalNode, parentToCompare.getUnresolvedSuperClass(), null);
         } else {
             if (interfacesToCompare != null && interfacesToCompare.length > 0) {
@@ -1517,7 +1557,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
     @Override
     public void visitCatchStatement(final CatchStatement cs) {
         resolveOrFail(cs.getExceptionType(), cs);
-        if (cs.getExceptionType() == ClassHelper.DYNAMIC_TYPE) {
+        if (TypeUtil.isDynamicTyped(cs.getExceptionType())) {
             cs.getVariable().setType(ClassHelper.make(Exception.class));
         }
         super.visitCatchStatement(cs);
@@ -1616,6 +1656,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
         ClassNode type = genericsType.getType();
         // save name before redirect
         GenericsTypeName name = new GenericsTypeName(type.getName());
+        visitTypeAnnotations(type);
         ClassNode[] bounds = genericsType.getUpperBounds();
         if (!genericParameterNames.containsKey(name)) {
             if (bounds != null) {
diff --git a/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java b/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java
index 422ab8a..de33450 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java
@@ -565,7 +565,7 @@ public class ProxyGeneratorAdapter extends ClassVisitor {
             Type[] args = Type.getArgumentTypes(desc);
             if (emptyBody) {
                 Type returnType = Type.getReturnType(desc);
-                if (returnType == Type.VOID_TYPE) {
+                if (returnType.equals(Type.VOID_TYPE)) {
                     mv.visitInsn(RETURN);
                 } else {
                     int loadIns = getLoadInsn(returnType);
diff --git a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
index 0bce9d4..f1cbcec 100644
--- a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
+++ b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
@@ -79,6 +79,15 @@ import java.util.stream.Stream;
 import static org.apache.groovy.ast.tools.ConstructorNodeUtils.getFirstIfSpecialConstructorCall;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveByte;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveChar;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveFloat;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveInt;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveShort;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 
 public class JavaStubGenerator {
     private final boolean java5;
@@ -523,7 +532,7 @@ public class JavaStubGenerator {
                 // GROOVY-5150 : Initialize value with a dummy constant so that Java cross compiles correctly
                 if (ClassHelper.STRING_TYPE.equals(valueExpr.getType())) {
                     out.print(formatString(valueExpr.getText()));
-                } else if (ClassHelper.char_TYPE.equals(valueExpr.getType())) {
+                } else if (isPrimitiveChar(valueExpr.getType())) {
                     out.print("'"+valueExpr.getText()+"'");
                 } else {
                     ClassNode constantType = valueExpr.getType();
@@ -534,7 +543,7 @@ public class JavaStubGenerator {
                     if (ClassHelper.Long_TYPE.equals(ClassHelper.getWrapper(constantType))) out.print('L');
                 }
             } else if (ClassHelper.isPrimitiveType(type)) {
-                String val = type == ClassHelper.boolean_TYPE ? "false" : "0";
+                String val = isPrimitiveBoolean(type) ? "false" : "0";
                 out.print("new " + ClassHelper.getWrapper(type) + "((" + type + ")" + val + ")");
             } else {
                 out.print("null");
@@ -818,7 +827,7 @@ public class JavaStubGenerator {
     }
 
     private void printReturn(PrintWriter out, ClassNode retType) {
-        if (!ClassHelper.VOID_TYPE.equals(retType)) {
+        if (!isPrimitiveVoid(retType)) {
             out.print("return ");
             printDefaultValue(out, retType);
             out.print(";");
@@ -826,14 +835,14 @@ public class JavaStubGenerator {
     }
 
     private void printDefaultValue(final PrintWriter out, final ClassNode type) {
-        if (!type.equals(ClassHelper.boolean_TYPE)) {
+        if (!isPrimitiveBoolean(type)) {
             out.print("(");
             printType(out, type);
             out.print(")");
         }
 
         if (ClassHelper.isPrimitiveType(type)) {
-            if (type.equals(ClassHelper.boolean_TYPE)) {
+            if (isPrimitiveBoolean(type)) {
                 out.print("false");
             } else {
                 out.print("0");
@@ -856,21 +865,21 @@ public class JavaStubGenerator {
 
     private void printTypeName(PrintWriter out, ClassNode type) {
         if (ClassHelper.isPrimitiveType(type)) {
-            if (type == ClassHelper.boolean_TYPE) {
+            if (isPrimitiveBoolean(type)) {
                 out.print("boolean");
-            } else if (type == ClassHelper.char_TYPE) {
+            } else if (isPrimitiveChar(type)) {
                 out.print("char");
-            } else if (type == ClassHelper.int_TYPE) {
+            } else if (isPrimitiveInt(type)) {
                 out.print("int");
-            } else if (type == ClassHelper.short_TYPE) {
+            } else if (isPrimitiveShort(type)) {
                 out.print("short");
-            } else if (type == ClassHelper.long_TYPE) {
+            } else if (isPrimitiveLong(type)) {
                 out.print("long");
-            } else if (type == ClassHelper.float_TYPE) {
+            } else if (isPrimitiveFloat(type)) {
                 out.print("float");
-            } else if (type == ClassHelper.double_TYPE) {
+            } else if (isPrimitiveDouble(type)) {
                 out.print("double");
-            } else if (type == ClassHelper.byte_TYPE) {
+            } else if (isPrimitiveByte(type)) {
                 out.print("byte");
             } else {
                 out.print("void");
diff --git a/src/main/java/org/codehaus/groovy/transform/AutoCloneASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/AutoCloneASTTransformation.java
index 49c58aa..2b64793 100644
--- a/src/main/java/org/codehaus/groovy/transform/AutoCloneASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/AutoCloneASTTransformation.java
@@ -184,7 +184,7 @@ public class AutoCloneASTTransformation extends AbstractASTTransformation {
             BlockStatement initBody = new BlockStatement();
             Parameter initParam = param(GenericsUtils.nonGeneric(cNode), "other");
             final Expression other = varX(initParam);
-            boolean hasParent = cNode.getSuperClass() != ClassHelper.OBJECT_TYPE;
+            boolean hasParent = !cNode.getSuperClass().equals(ClassHelper.OBJECT_TYPE);
             if (hasParent) {
                 initBody.addStatement(stmt(ctorX(ClassNode.SUPER, other)));
             }
@@ -244,7 +244,7 @@ public class AutoCloneASTTransformation extends AbstractASTTransformation {
     private static void addSimpleCloneHelperMethod(ClassNode cNode, List<FieldNode> fieldNodes, List<String> excludes) {
         Parameter methodParam = new Parameter(GenericsUtils.nonGeneric(cNode), "other");
         final Expression other = varX(methodParam);
-        boolean hasParent = cNode.getSuperClass() != ClassHelper.OBJECT_TYPE;
+        boolean hasParent = !cNode.getSuperClass().equals(ClassHelper.OBJECT_TYPE);
         BlockStatement methodBody = new BlockStatement();
         if (hasParent) {
             methodBody.addStatement(stmt(callSuperX("cloneOrCopyMembers", args(other))));
diff --git a/src/main/java/org/codehaus/groovy/transform/AutoImplementASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/AutoImplementASTTransformation.java
index 040d084..501ab4a 100644
--- a/src/main/java/org/codehaus/groovy/transform/AutoImplementASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/AutoImplementASTTransformation.java
@@ -57,6 +57,7 @@ import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpecRecurse;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
 import static org.codehaus.groovy.ast.tools.ParameterUtils.parametersEqual;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
 
 /**
  * Generates code for the {@code @AutoImplement} annotation.
@@ -201,7 +202,7 @@ public class AutoImplementASTTransformation extends AbstractASTTransformation {
                 if (!pn.getField().isFinal()) {
                     result.remove(pn.getSetterNameOrDefault() + ":" + pn.getType().getText() + ",");
                 }
-                if (!pn.getType().equals(ClassHelper.boolean_TYPE)) {
+                if (!isPrimitiveBoolean(pn.getType())) {
                     result.remove(pn.getGetterNameOrDefault() + ":");
                 } else if (pn.getGetterName() != null) {
                     result.remove(pn.getGetterName() + ":");
diff --git a/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java
index 4cfdf63..7d01e19 100644
--- a/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java
@@ -67,6 +67,7 @@ import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpecRecurse;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.extractSuperClassGenerics;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
 import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
 import static org.objectweb.asm.Opcodes.ACC_NATIVE;
@@ -222,8 +223,7 @@ public class DelegateASTTransformation extends AbstractASTTransformation {
                 mNames.add(getSetterName(name));
             }
             mNames.add(getGetterName(name));
-            boolean isPrimBool = pNode.getOriginType().equals(ClassHelper.boolean_TYPE);
-            if (isPrimBool) {
+            if (isPrimitiveBoolean(pNode.getOriginType())) {
                 mNames.add(getPredicateName(name));
             }
         }
@@ -256,7 +256,7 @@ public class DelegateASTTransformation extends AbstractASTTransformation {
     }
 
     private static void addGetterIfNeeded(final DelegateDescription delegate, final PropertyNode prop, final String name, final boolean allNames) {
-        boolean isPrimBool = prop.getOriginType().equals(ClassHelper.boolean_TYPE);
+        boolean isPrimBool = isPrimitiveBoolean(prop.getOriginType());
         // do a little bit of pre-work since Groovy compiler hasn't added property accessors yet
         boolean willHaveGetAccessor = true;
         boolean willHaveIsAccessor = isPrimBool;
@@ -303,7 +303,7 @@ public class DelegateASTTransformation extends AbstractASTTransformation {
         boolean hasIsAccessor = owner.getGetterMethod(getPredicateName(name)) != null;
         PropertyNode prop = owner.getProperty(name);
         willHaveGetAccessor.set(hasGetAccessor || (prop != null && !hasIsAccessor));
-        willHaveIsAccessor.set(hasIsAccessor || (prop != null && !hasGetAccessor && prop.getOriginType().equals(ClassHelper.boolean_TYPE)));
+        willHaveIsAccessor.set(hasIsAccessor || (prop != null && !hasGetAccessor && isPrimitiveBoolean(prop.getOriginType())));
     }
 
     private static boolean shouldSkipPropertyMethod(final String propertyName, final String methodName, final List<String> excludes, final List<String> includes, final boolean allNames) {
diff --git a/src/main/java/org/codehaus/groovy/transform/ExternalizeMethodsASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/ExternalizeMethodsASTTransformation.java
index 4b7a21e..c2ef82c 100644
--- a/src/main/java/org/codehaus/groovy/transform/ExternalizeMethodsASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/ExternalizeMethodsASTTransformation.java
@@ -49,6 +49,13 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveByte;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveFloat;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveInt;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveShort;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
 import static org.objectweb.asm.Opcodes.ACC_TRANSIENT;
 
@@ -117,15 +124,15 @@ public class ExternalizeMethodsASTTransformation extends AbstractASTTransformati
 
     private static String suffixForField(FieldNode fNode) {
         // use primitives for efficiency
-        if (fNode.getType() == ClassHelper.int_TYPE) return "Int";
-        if (fNode.getType() == ClassHelper.boolean_TYPE) return "Boolean";
+        if (isPrimitiveInt(fNode.getType())) return "Int";
+        if (isPrimitiveBoolean(fNode.getType())) return "Boolean";
 //        currently char isn't found due to a bug, so go with Object
-//        if (fNode.getType() == ClassHelper.char_TYPE) return "Char";
-        if (fNode.getType() == ClassHelper.long_TYPE) return "Long";
-        if (fNode.getType() == ClassHelper.short_TYPE) return "Short";
-        if (fNode.getType() == ClassHelper.byte_TYPE) return "Byte";
-        if (fNode.getType() == ClassHelper.float_TYPE) return "Float";
-        if (fNode.getType() == ClassHelper.double_TYPE) return "Double";
+//        if (isPrimitiveChar(fNode.getType())) return "Character";
+        if (isPrimitiveLong(fNode.getType())) return "Long";
+        if (isPrimitiveShort(fNode.getType())) return "Short";
+        if (isPrimitiveByte(fNode.getType())) return "Byte";
+        if (isPrimitiveFloat(fNode.getType())) return "Float";
+        if (isPrimitiveDouble(fNode.getType())) return "Double";
         return "Object";
     }
 }
diff --git a/src/main/java/org/codehaus/groovy/transform/LazyASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/LazyASTTransformation.java
index 9251d0b..71a0d87 100644
--- a/src/main/java/org/codehaus/groovy/transform/LazyASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/LazyASTTransformation.java
@@ -64,6 +64,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
 import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
 import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
@@ -178,7 +179,7 @@ public class LazyASTTransformation extends AbstractASTTransformation {
         String propName = capitalize(fieldNode.getName().substring(1));
         ClassNode declaringClass = fieldNode.getDeclaringClass();
         addGeneratedMethodOrError(declaringClass, "get" + propName, visibility, type, body, xform, fieldNode);
-        if (ClassHelper.boolean_TYPE.equals(type)) {
+        if (isPrimitiveBoolean(type)) {
             addGeneratedMethodOrError(declaringClass, "is" + propName, visibility, type, stmt(callThisX("get" + propName)), xform, fieldNode);
         }
         // expect no setter
diff --git a/src/main/java/org/codehaus/groovy/transform/sc/transformers/BooleanExpressionTransformer.java b/src/main/java/org/codehaus/groovy/transform/sc/transformers/BooleanExpressionTransformer.java
index 575c532..412a014 100644
--- a/src/main/java/org/codehaus/groovy/transform/sc/transformers/BooleanExpressionTransformer.java
+++ b/src/main/java/org/codehaus/groovy/transform/sc/transformers/BooleanExpressionTransformer.java
@@ -41,6 +41,7 @@ import java.lang.reflect.Modifier;
 import java.util.Iterator;
 import java.util.List;
 
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments;
 import static org.objectweb.asm.Opcodes.DUP;
 import static org.objectweb.asm.Opcodes.GOTO;
@@ -110,7 +111,7 @@ public class BooleanExpressionTransformer {
                 WriterController controller = acg.getController();
                 OperandStack os = controller.getOperandStack();
 
-                if (type.equals(ClassHelper.boolean_TYPE)) {
+                if (isPrimitiveBoolean(type)) {
                     expression.visit(visitor);
                     os.doGroovyCast(ClassHelper.boolean_TYPE);
                     return;
diff --git a/src/main/java/org/codehaus/groovy/transform/sc/transformers/MethodCallExpressionTransformer.java b/src/main/java/org/codehaus/groovy/transform/sc/transformers/MethodCallExpressionTransformer.java
index 5dd83ad..356ea77 100644
--- a/src/main/java/org/codehaus/groovy/transform/sc/transformers/MethodCallExpressionTransformer.java
+++ b/src/main/java/org/codehaus/groovy/transform/sc/transformers/MethodCallExpressionTransformer.java
@@ -90,11 +90,11 @@ public class MethodCallExpressionTransformer {
                         Expression indexExpr = argList.get(0);
                         ClassNode argType = staticCompilationTransformer.getTypeChooser().resolveType(indexExpr, staticCompilationTransformer.getClassNode());
                         ClassNode indexType = ClassHelper.getWrapper(argType);
-                        if (componentType.isEnum() && ClassHelper.Number_TYPE == indexType) {
+                        if (componentType.isEnum() && ClassHelper.Number_TYPE.equals(indexType)) {
                             // workaround for generated code in enums which use .next() returning a Number
                             indexType = ClassHelper.Integer_TYPE;
                         }
-                        if (argType != null && ClassHelper.Integer_TYPE == indexType) {
+                        if (argType != null && ClassHelper.Integer_TYPE.equals(indexType)) {
                             BinaryExpression binaryExpression = new BinaryExpression(
                                     objectExpression,
                                     Token.newSymbol("[", indexExpr.getLineNumber(), indexExpr.getColumnNumber()),
@@ -113,7 +113,7 @@ public class MethodCallExpressionTransformer {
                         Expression indexExpr = argList.get(0);
                         Expression objExpr = argList.get(1);
                         ClassNode argType = staticCompilationTransformer.getTypeChooser().resolveType(indexExpr, staticCompilationTransformer.getClassNode());
-                        if (argType != null && ClassHelper.Integer_TYPE == ClassHelper.getWrapper(argType)) {
+                        if (argType != null && ClassHelper.Integer_TYPE.equals(ClassHelper.getWrapper(argType))) {
                             BinaryExpression arrayGet = new BinaryExpression(
                                     objectExpression,
                                     Token.newSymbol("[", indexExpr.getLineNumber(), indexExpr.getColumnNumber()),
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/Receiver.java b/src/main/java/org/codehaus/groovy/transform/stc/Receiver.java
index ad934ee..2036926 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/Receiver.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/Receiver.java
@@ -26,7 +26,7 @@ public class Receiver<T> {
     private final T data;
 
     public static <T> Receiver<T> make(final ClassNode type) {
-        return new Receiver<T>(type==null?ClassHelper.OBJECT_TYPE:type);
+        return new Receiver<T>(type == null ? ClassHelper.OBJECT_TYPE.getPlainNodeReference() : type);
     }
 
     public Receiver(final ClassNode type) {
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index 0ff7e62..21b40c0 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -112,6 +112,7 @@ import static org.codehaus.groovy.ast.ClassHelper.make;
 import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching;
 import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.void_WRAPPER_TYPE;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
 import static org.codehaus.groovy.runtime.DefaultGroovyMethodsSupport.closeQuietly;
 import static org.codehaus.groovy.syntax.Types.BITWISE_AND;
@@ -760,7 +761,7 @@ public abstract class StaticTypeCheckingSupport {
     public static boolean isWildcardLeftHandSide(final ClassNode node) {
         return (OBJECT_TYPE.equals(node)
                 || STRING_TYPE.equals(node)
-                || boolean_TYPE.equals(node)
+                || isPrimitiveBoolean(node)
                 || Boolean_TYPE.equals(node)
                 || CLASS_Type.equals(node));
     }
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 4861464..0cf11a7 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -101,6 +101,7 @@ import org.codehaus.groovy.ast.tools.GenericsUtils;
 import org.codehaus.groovy.ast.tools.WideningCategories;
 import org.codehaus.groovy.classgen.ReturnAdder;
 import org.codehaus.groovy.classgen.asm.InvocationWriter;
+import org.codehaus.groovy.classgen.asm.util.TypeUtil;
 import org.codehaus.groovy.control.CompilationUnit;
 import org.codehaus.groovy.control.ErrorCollector;
 import org.codehaus.groovy.control.ResolveVisitor;
@@ -210,6 +211,15 @@ import static org.codehaus.groovy.ast.tools.WideningCategories.isLongCategory;
 import static org.codehaus.groovy.ast.tools.WideningCategories.isNumberCategory;
 import static org.codehaus.groovy.ast.tools.WideningCategories.lowestUpperBound;
 import static org.codehaus.groovy.classgen.AsmClassGenerator.MINIMUM_BYTECODE_VERSION;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveByte;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveChar;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveDouble;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveFloat;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveInt;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveLong;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveShort;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 import static org.codehaus.groovy.syntax.Types.ASSIGN;
 import static org.codehaus.groovy.syntax.Types.COMPARE_EQUAL;
 import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_EQUAL;
@@ -1375,7 +1385,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
      * @param arguments the constructor arguments
      */
     protected MethodNode checkGroovyStyleConstructor(final ClassNode node, final ClassNode[] arguments, final ASTNode source) {
-        if (node.equals(OBJECT_TYPE) || node.equals(DYNAMIC_TYPE)) {
+        if (node.equals(OBJECT_TYPE) || TypeUtil.isDynamicTyped(node)) {
             // in that case, we are facing a list constructor assigned to a def or object
             return null;
         }
@@ -1915,7 +1925,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             if (!checkCompatibleAssignmentTypes(forLoopVariableType, componentType)) {
                 addStaticTypeError("Cannot loop with element of type " + prettyPrintType(forLoopVariableType) + " with collection of type " + prettyPrintType(collectionType), forLoop);
             }
-            if (forLoopVariableType != DYNAMIC_TYPE) {
+            if (!TypeUtil.isDynamicTyped(forLoopVariableType)) {
                 // user has specified a type, prefer it over the inferred type
                 componentType = forLoopVariableType;
             }
@@ -1995,9 +2005,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         if (isBigIntCategory(typeRe)) {
             // allow any internal number that is not a floating point one
             resultType = type;
-        } else if (typeRe == STRING_TYPE || typeRe == GSTRING_TYPE) {
+        } else if (typeRe.equals(STRING_TYPE) || typeRe.equals(GSTRING_TYPE)) {
             resultType = PATTERN_TYPE;
-        } else if (typeRe == ArrayList_TYPE) {
+        } else if (typeRe.equals(ArrayList_TYPE)) {
             resultType = ArrayList_TYPE;
         } else if (typeRe.equals(PATTERN_TYPE)) {
             resultType = PATTERN_TYPE;
@@ -2094,13 +2104,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     private static ClassNode getMathWideningClassNode(final ClassNode type) {
-        if (byte_TYPE.equals(type) || short_TYPE.equals(type) || int_TYPE.equals(type)) {
+        if (isPrimitiveByte(type) || isPrimitiveShort(type) || isPrimitiveInt(type)) {
             return int_TYPE;
         }
         if (Byte_TYPE.equals(type) || Short_TYPE.equals(type) || Integer_TYPE.equals(type)) {
             return Integer_TYPE;
         }
-        if (float_TYPE.equals(type)) {
+        if (isPrimitiveFloat(type)) {
             return double_TYPE;
         }
         if (Float_TYPE.equals(type)) {
@@ -2115,7 +2125,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         ClassNode resultType;
         if (isDoubleCategory(getUnwrapper(typeRe))) {
             resultType = type;
-        } else if (typeRe == ArrayList_TYPE) {
+        } else if (typeRe.equals(ArrayList_TYPE)) {
             resultType = ArrayList_TYPE;
         } else {
             MethodNode mn = findMethodOrFail(expression, type, name);
@@ -2191,7 +2201,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
         if (enclosingMethod != null && !enclosingMethod.isVoidMethod()) {
             if (!isNullConstant(expression)
-                    && !type.equals(VOID_TYPE)
+                    && !isPrimitiveVoid(type)
                     && !type.equals(void_WRAPPER_TYPE)
                     && !checkCompatibleAssignmentTypes(enclosingMethod.getReturnType(), type, null, false)) {
                 if (!extension.handleIncompatibleReturnType(statement, type)) {
@@ -2218,7 +2228,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     protected void addClosureReturnType(final ClassNode returnType) {
-        if (returnType != null && !returnType.equals(VOID_TYPE)) // GROOVY-8202
+        if (returnType != null && !isPrimitiveVoid(returnType)) // GROOVY-8202
             typeCheckingContext.getEnclosingClosure().addReturnType(returnType);
     }
 
@@ -4151,19 +4161,19 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         ClassNode expressionType = getType(source);
         if (targetType.isArray() && expressionType.isArray()) {
             return checkCast(targetType.getComponentType(), varX("foo", expressionType.getComponentType()));
-        } else if (targetType.equals(char_TYPE) && expressionType == STRING_TYPE
+        } else if (isPrimitiveChar(targetType) && expressionType.equals(STRING_TYPE)
                 && source instanceof ConstantExpression && source.getText().length() == 1) {
             // ex: (char) 'c'
-        } else if (targetType.equals(Character_TYPE) && (expressionType == STRING_TYPE || sourceIsNull)
+        } else if (targetType.equals(Character_TYPE) && (expressionType.equals(STRING_TYPE) || sourceIsNull)
                 && (sourceIsNull || source instanceof ConstantExpression && source.getText().length() == 1)) {
             // ex : (Character) 'c'
-        } else if (isNumberCategory(getWrapper(targetType)) && (isNumberCategory(getWrapper(expressionType)) || char_TYPE == expressionType)) {
+        } else if (isNumberCategory(getWrapper(targetType)) && (isNumberCategory(getWrapper(expressionType)) || isPrimitiveChar(expressionType))) {
             // ex: short s = (short) 0
         } else if (sourceIsNull && !isPrimitiveType(targetType)) {
             // ex: (Date)null
-        } else if (char_TYPE == targetType && isPrimitiveType(expressionType) && isNumberType(expressionType)) {
+        } else if (isPrimitiveChar(targetType) && isPrimitiveType(expressionType) && isNumberType(expressionType)) {
             // char c = (char) ...
-        } else if (sourceIsNull && isPrimitiveType(targetType) && !boolean_TYPE.equals(targetType)) {
+        } else if (sourceIsNull && isPrimitiveType(targetType) && !isPrimitiveBoolean(targetType)) {
             return false;
         } else if ((expressionType.getModifiers() & Opcodes.ACC_FINAL) == 0 && targetType.isInterface()) {
             return true;
@@ -4505,7 +4515,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             } else if (isCompareToBoolean(op) || op == COMPARE_EQUAL || op == COMPARE_NOT_EQUAL) {
                 return boolean_TYPE;
             }
-        } else if (char_TYPE.equals(leftRedirect) && char_TYPE.equals(rightRedirect)) {
+        } else if (isPrimitiveChar(leftRedirect) && isPrimitiveChar(rightRedirect)) {
             if (isCompareToBoolean(op) || op == COMPARE_EQUAL || op == COMPARE_NOT_EQUAL) {
                 return boolean_TYPE;
             }
@@ -4573,19 +4583,19 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             if (isBigIntCategory(a) && isBigIntCategory(b)) return BigInteger_TYPE;
             return BigDecimal_TYPE;
         }
-        if (double_TYPE.equals(a) || double_TYPE.equals(b)) return double_TYPE;
+        if (isPrimitiveDouble(a) || isPrimitiveDouble(b)) return double_TYPE;
         if (Double_TYPE.equals(a) || Double_TYPE.equals(b)) return Double_TYPE;
-        if (float_TYPE.equals(a) || float_TYPE.equals(b)) return float_TYPE;
+        if (isPrimitiveFloat(a) || isPrimitiveFloat(b)) return float_TYPE;
         if (Float_TYPE.equals(a) || Float_TYPE.equals(b)) return Float_TYPE;
-        if (long_TYPE.equals(a) || long_TYPE.equals(b)) return long_TYPE;
+        if (isPrimitiveLong(a) || isPrimitiveLong(b)) return long_TYPE;
         if (Long_TYPE.equals(a) || Long_TYPE.equals(b)) return Long_TYPE;
-        if (int_TYPE.equals(a) || int_TYPE.equals(b)) return int_TYPE;
+        if (isPrimitiveInt(a) || isPrimitiveInt(b)) return int_TYPE;
         if (Integer_TYPE.equals(a) || Integer_TYPE.equals(b)) return Integer_TYPE;
-        if (short_TYPE.equals(a) || short_TYPE.equals(b)) return short_TYPE;
+        if (isPrimitiveShort(a) || isPrimitiveShort(b)) return short_TYPE;
         if (Short_TYPE.equals(a) || Short_TYPE.equals(b)) return Short_TYPE;
-        if (byte_TYPE.equals(a) || byte_TYPE.equals(b)) return byte_TYPE;
+        if (isPrimitiveByte(a) || isPrimitiveByte(b)) return byte_TYPE;
         if (Byte_TYPE.equals(a) || Byte_TYPE.equals(b)) return Byte_TYPE;
-        if (char_TYPE.equals(a) || char_TYPE.equals(b)) return char_TYPE;
+        if (isPrimitiveChar(a) || isPrimitiveChar(b)) return char_TYPE;
         if (Character_TYPE.equals(a) || Character_TYPE.equals(b)) return Character_TYPE;
         return Number_TYPE;
     }
diff --git a/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java b/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
index 5cff9a5..1b8b00d 100644
--- a/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
+++ b/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
@@ -42,6 +42,8 @@ import java.util.function.Function;
 
 import static org.codehaus.groovy.ast.tools.GeneralUtils.thisPropX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
 import static org.objectweb.asm.Opcodes.ACC_STATIC;
@@ -162,7 +164,7 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
                 for (MethodNode method : helperType.getMethods(getterName)) {
                     if (method.isStatic() && method.getParameters().length == 1
                             && isSelfType(method.getParameters()[0], traitType)
-                            && !method.getReturnType().equals(ClassHelper.VOID_TYPE)) {
+                            && !isPrimitiveVoid(method.getReturnType())) {
                         return xform.apply(method);
                     }
                 }
@@ -171,7 +173,7 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
                 for (MethodNode method : helperType.getMethods(isserName)) {
                     if (method.isStatic() && method.getParameters().length == 1
                             && isSelfType(method.getParameters()[0], traitType)
-                            && method.getReturnType().equals(ClassHelper.boolean_TYPE)) {
+                            && isPrimitiveBoolean(method.getReturnType())) {
                         return xform.apply(method);
                     }
                 }
diff --git a/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
index 5191cf4..aac32e2 100644
--- a/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
@@ -75,6 +75,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
 import static org.codehaus.groovy.transform.trait.SuperCallTraitTransformer.UNRESOLVED_HELPER_CLASS;
 import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
@@ -400,7 +401,7 @@ public class TraitASTTransformation extends AbstractASTTransformation implements
         Statement getterBlock = node.getGetterBlock();
         if (getterBlock == null) {
             MethodNode getter = cNode.getGetterMethod(getterName);
-            if (getter == null && node.getType().equals(ClassHelper.boolean_TYPE)) {
+            if (getter == null && isPrimitiveBoolean(node.getType())) {
                 getter = cNode.getGetterMethod("is" + capitalize(name));
             }
             if (!node.isPrivate() && methodNeedsReplacement(cNode, getter)) {
@@ -422,7 +423,7 @@ public class TraitASTTransformation extends AbstractASTTransformation implements
             getter.setSynthetic(true);
             cNode.addMethod(getter);
 
-            if (node.getGetterName() == null && getterName.startsWith("get") && (node.getType().equals(ClassHelper.boolean_TYPE) || node.getType().equals(ClassHelper.Boolean_TYPE))) {
+            if (node.getGetterName() == null && getterName.startsWith("get") && (isPrimitiveBoolean(node.getType()) || node.getType().equals(ClassHelper.Boolean_TYPE))) {
                 MethodNode secondGetter = new MethodNode("is" + capitalize(name), propNodeModifiers, node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock);
                 if (methodNeedsReplacement(cNode, secondGetter)) {
                     secondGetter.setSynthetic(true);
diff --git a/src/test/org/codehaus/groovy/ast/MethodNodeTest.groovy b/src/test/org/codehaus/groovy/ast/MethodNodeTest.groovy
index a8d51a2..bda1c67 100644
--- a/src/test/org/codehaus/groovy/ast/MethodNodeTest.groovy
+++ b/src/test/org/codehaus/groovy/ast/MethodNodeTest.groovy
@@ -65,7 +65,7 @@ class MethodNodeTest extends TestCase implements Opcodes {
         assert !methodNode.isDynamicReturnType()
     }
     
-    void testIsDynamicReturnTypNull() {
+    void testIsDynamicReturnTypeNull() {
         MethodNode methodNode = new MethodNode('foo', ACC_PUBLIC, null, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement())
         assert !methodNode.isDynamicReturnType()
         assertNotNull(methodNode.getReturnType())
diff --git a/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
new file mode 100644
index 0000000..6e806ae
--- /dev/null
+++ b/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
@@ -0,0 +1,253 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *    http://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.codehaus.groovy.classgen.asm
+
+class TypeAnnotationsTest extends AbstractBytecodeTestCase {
+    void testTypeAnnotationsForMethod1() {
+        assert compile(method: 'foo','''
+        import java.lang.annotation.*
+        import static java.lang.annotation.RetentionPolicy.RUNTIME
+        import static java.lang.annotation.ElementType.*
+
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno0 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno1 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno2 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno3 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno4 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno5 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno6 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno7 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno8 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno9 { }
+
+        <@TypeAnno0 X extends @TypeAnno1 Number> @TypeAnno2 List<@TypeAnno3 X> foo(
+            @TypeAnno4 List<@TypeAnno5 ? super @TypeAnno6 Map<@TypeAnno7 X, @TypeAnno8 ?>> arg
+        ) throws @TypeAnno9 Exception {}
+        ''').hasSequence([
+                'public foo(Ljava/util/List;)Ljava/util/List;',
+                '@LTypeAnno0;() : METHOD_TYPE_PARAMETER 0, null',
+                '@LTypeAnno1;() : METHOD_TYPE_PARAMETER_BOUND 0, 0, null',
+                '@LTypeAnno2;() : METHOD_RETURN, null',
+                '@LTypeAnno3;() : METHOD_RETURN, 0;',
+                '@LTypeAnno4;() : METHOD_FORMAL_PARAMETER 0, null',
+                '@LTypeAnno5;() : METHOD_FORMAL_PARAMETER 0, 0;',
+                '@LTypeAnno6;() : METHOD_FORMAL_PARAMETER 0, 0;*',
+                '@LTypeAnno7;() : METHOD_FORMAL_PARAMETER 0, 0;*0;',
+                '@LTypeAnno8;() : METHOD_FORMAL_PARAMETER 0, 0;*1;',
+                '@LTypeAnno9;() : THROWS 0, null'
+        ])
+    }
+
+    void testTypeAnnotationsForMethod2() {
+        assert compile(method: 'foo','''
+        import java.lang.annotation.*
+        import static java.lang.annotation.RetentionPolicy.RUNTIME
+        import static java.lang.annotation.ElementType.*
+
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeParameterAnno { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno0 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno1 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno2 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno3 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno4 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno5 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno6 { }
+
+        <@TypeAnno0 @TypeParameterAnno E extends @TypeAnno1 Number & @TypeAnno2 List<@TypeAnno3 E>> void foo(
+            @TypeAnno4 E arg1, @TypeAnno5 int arg2, @TypeAnno6 int arg3
+        ) {
+            // TODO support in code examples below
+            try {
+                numbers.addAll(new @TypeAnno5 ArrayList<@TypeAnno6 Integer>());
+            } catch (@TypeAnno7 Throwable ignore) {
+            }
+        }
+        ''').hasSequence([
+                'public foo(Ljava/lang/Number;II)V',
+                '@LTypeAnno0;() : METHOD_TYPE_PARAMETER 0, null',
+                '@LTypeParameterAnno;() : METHOD_TYPE_PARAMETER 0, null',
+                '@LTypeAnno1;() : METHOD_TYPE_PARAMETER_BOUND 0, 0, null',
+                '@LTypeAnno2;() : METHOD_TYPE_PARAMETER_BOUND 0, 1, null',
+                '@LTypeAnno3;() : METHOD_TYPE_PARAMETER_BOUND 0, 1, 0;',
+                '@LTypeAnno4;() : METHOD_FORMAL_PARAMETER 0, null',
+                '@LTypeAnno5;() : METHOD_FORMAL_PARAMETER 1, null',
+                '@LTypeAnno6;() : METHOD_FORMAL_PARAMETER 2, null'
+        ])
+    }
+
+    void testTypeAnnotationsForMethod3() {
+        assert compile(method: 'get', classNamePattern: 'Foo','''
+        import java.lang.annotation.*
+        import static java.lang.annotation.RetentionPolicy.RUNTIME
+        import static java.lang.annotation.ElementType.*
+
+        @Retention(RUNTIME) @Target([PARAMETER]) @interface ParameterAnno { }
+        @Retention(RUNTIME) @Target([TYPE_PARAMETER, TYPE_USE]) @interface TypeUseAndParameterAnno { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno0 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno1 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno2 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno3 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno4 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno5 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno6 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno7 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno8 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno9 { }
+
+        class Foo<X, Y> {
+            @TypeAnno0 Map<@TypeAnno1 ? extends X, ? super @TypeAnno2 Y>
+            get(@TypeAnno3 @ParameterAnno @TypeUseAndParameterAnno List<@TypeAnno4 List<@TypeAnno5 X>> arg1,
+                Map<@TypeAnno6 ? extends @TypeAnno7 X, @TypeAnno8 ? super @TypeAnno9 Y> arg2
+            ) throws @TypeAnno0 RuntimeException, @TypeAnno1 IOException {
+                // TODO support in code example below
+                @TypeAnno0 List<@TypeAnno1 Integer> numbers = Arrays.<@TypeAnno3 Integer>asList(5, 3, 50, 24, 40, 2, 9, 18);
+                if (arg2 instanceof @TypeAnno2 Number) {
+                    return (@TypeAnno3 Map<@TypeAnno4 ? extends X, ? super @TypeAnno5 Y>) null;
+                }
+                //if (numbers.stream().sorted(@TypeAnno6 Integer::compareTo).count() > 0) return null; // needs grammar tweak
+                return arg2;
+            }
+        }
+        ''').hasSequence([
+                'public get(Ljava/util/List;Ljava/util/Map;)Ljava/util/Map; throws java/lang/RuntimeException',
+                '@LTypeAnno0;() : METHOD_RETURN, null',
+                '@LTypeAnno1;() : METHOD_RETURN, 0;',
+                '@LTypeAnno2;() : METHOD_RETURN, 1;*',
+                '@LTypeAnno3;() : METHOD_FORMAL_PARAMETER 0, null',
+                '@LTypeUseAndParameterAnno;() : METHOD_FORMAL_PARAMETER 0, null',
+                '@LTypeAnno4;() : METHOD_FORMAL_PARAMETER 0, 0;',
+                '@LTypeAnno5;() : METHOD_FORMAL_PARAMETER 0, 0;0;',
+                '@LTypeAnno6;() : METHOD_FORMAL_PARAMETER 1, 0;',
+                '@LTypeAnno7;() : METHOD_FORMAL_PARAMETER 1, 0;*',
+                '@LTypeAnno8;() : METHOD_FORMAL_PARAMETER 1, 1;',
+                '@LTypeAnno9;() : METHOD_FORMAL_PARAMETER 1, 1;*',
+                '@LTypeAnno0;() : THROWS 0, null',
+                '@LTypeAnno1;() : THROWS 1, null'
+        ])
+    }
+
+    void testTypeAnnotationsForConstructor() {
+        assert compile(method: '<init>', classNamePattern: 'HasCons', '''
+        import java.lang.annotation.*
+        import static java.lang.annotation.RetentionPolicy.RUNTIME
+        import static java.lang.annotation.ElementType.*
+
+        @Retention(RUNTIME) @Target([CONSTRUCTOR]) @interface ConstructorAnno { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno0 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno1 { }
+
+        class HasCons {
+            @ConstructorAnno @TypeAnno0 @TypeAnno1 HasCons() { }
+        }
+        ''').hasSequence([
+                'public <init>()V',
+                '@LConstructorAnno;()',
+                '@LTypeAnno0;() : METHOD_RETURN',
+                '@LTypeAnno1;() : METHOD_RETURN'
+        ])
+    }
+
+    void testTypeAnnotationsForField1() {
+        assert compile(field: 'documents', classNamePattern: 'Foo', '''
+        import java.lang.annotation.*
+        import static java.lang.annotation.RetentionPolicy.RUNTIME
+        import static java.lang.annotation.ElementType.*
+
+        @Retention(RUNTIME) @Target([FIELD]) @interface FieldAnno { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno0 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno1 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno2 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno3 { }
+
+        class Foo {
+            public @FieldAnno Map<@TypeAnno0 ? extends @TypeAnno1 CharSequence, @TypeAnno2 List<@TypeAnno3 ?>> documents = null
+        }
+        ''').hasSequence([
+                'public Ljava/util/Map; documents',
+                '@LFieldAnno;()',
+                '@LTypeAnno0;() : FIELD, 0;',
+                '@LTypeAnno1;() : FIELD, 0;*',
+                '@LTypeAnno2;() : FIELD, 1;',
+                '@LTypeAnno3;() : FIELD, 1;0;'
+        ])
+    }
+
+    void testTypeAnnotationsForField2() {
+        assert compile(field: 'numbers', classNamePattern: 'Bar','''
+        import java.lang.annotation.*
+        import static java.lang.annotation.RetentionPolicy.RUNTIME
+        import static java.lang.annotation.ElementType.*
+
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno4 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno5 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno6 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno7 { }
+
+        class Bar {
+            // TODO support type annotations for code (TypeAnno7 below)
+            public @TypeAnno4 List<@TypeAnno5 ? super @TypeAnno6 Integer> numbers = Arrays.<@TypeAnno7 Integer>asList(5, 3, 50, 24, 40, 2, 9, 18)
+        }
+        ''').hasSequence([
+                'public Ljava/util/List; numbers',
+                '@LTypeAnno4;() : FIELD, null',
+                '@LTypeAnno5;() : FIELD, 0;',
+                '@LTypeAnno6;() : FIELD, 0;*'
+        ])
+    }
+
+    void testTypeAnnotationsForClass() {
+        assert compile(classNamePattern: 'MyClass','''
+        import java.lang.annotation.*
+        import java.rmi.Remote
+        import static java.lang.annotation.RetentionPolicy.RUNTIME
+        import static java.lang.annotation.ElementType.*
+
+        @Retention(RUNTIME) @Target([TYPE]) @interface TypeAnno { }
+        @Retention(RUNTIME) @Target([TYPE_PARAMETER]) @interface TypeParameterAnno1 { }
+        @Retention(RUNTIME) @Target([TYPE_PARAMETER]) @interface TypeParameterAnno2 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno0 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno1 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno2 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno3 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno4 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno5 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno6 { }
+        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno7 { }
+
+        @TypeAnno @TypeUseAnno0 @TypeUseAnno1
+        class MyClass<@TypeParameterAnno1 @TypeParameterAnno2 X, @TypeParameterAnno2 Y extends @TypeUseAnno2 File>
+                extends @TypeUseAnno3 ArrayList<@TypeUseAnno4 X>
+                implements @TypeUseAnno5 Remote, @TypeUseAnno6 List<@TypeUseAnno7 X> { }
+        ''').hasSequence([
+                'public class MyClass extends java/util/ArrayList implements java/rmi/Remote java/util/List groovy/lang/GroovyObject {',
+                '@LTypeAnno;()',
+                '@LTypeUseAnno0;()',
+                '@LTypeUseAnno1;()',
+                '@LTypeParameterAnno1;() : CLASS_TYPE_PARAMETER 0, null',
+                '@LTypeParameterAnno2;() : CLASS_TYPE_PARAMETER 0, null',
+                '@LTypeParameterAnno2;() : CLASS_TYPE_PARAMETER 1, null',
+                '@LTypeUseAnno2;() : CLASS_TYPE_PARAMETER_BOUND 1, 0, null',
+                '@LTypeUseAnno3;() : CLASS_EXTENDS -1, null',
+                '@LTypeUseAnno4;() : CLASS_EXTENDS -1, 0;',
+                '@LTypeUseAnno5;() : CLASS_EXTENDS 0, null',
+                '@LTypeUseAnno6;() : CLASS_EXTENDS 1, null',
+                '@LTypeUseAnno7;() : CLASS_EXTENDS 1, 0;'
+        ])
+    }
+}
diff --git a/subprojects/groovy-console/src/test/groovy/groovy/console/ui/ScriptToTreeNodeAdapterTest.groovy b/subprojects/groovy-console/src/test/groovy/groovy/console/ui/ScriptToTreeNodeAdapterTest.groovy
index 6b065af..c5acf19 100644
--- a/subprojects/groovy-console/src/test/groovy/groovy/console/ui/ScriptToTreeNodeAdapterTest.groovy
+++ b/subprojects/groovy-console/src/test/groovy/groovy/console/ui/ScriptToTreeNodeAdapterTest.groovy
@@ -143,7 +143,7 @@ final class ScriptToTreeNodeAdapterTest extends GroovyTestCase {
                 [
                         eq('ClassNode - Foo'),
                         eq('Fields'),
-                        eq('FieldNode - aField : java.lang.Object'),
+                        startsWith('FieldNode - aField : java.lang.Object'),
                 ]
         )
     }
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationProcessorVisitor.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationProcessorVisitor.java
index 7364de9..6b69838 100644
--- a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationProcessorVisitor.java
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/ast/visitor/AnnotationProcessorVisitor.java
@@ -26,7 +26,6 @@ import org.apache.groovy.contracts.util.AnnotationUtils;
 import org.apache.groovy.contracts.util.Validate;
 import org.codehaus.groovy.GroovyBugError;
 import org.codehaus.groovy.ast.AnnotationNode;
-import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.ConstructorNode;
 import org.codehaus.groovy.ast.MethodNode;
@@ -51,6 +50,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 
 /**
  * Visits annotations of meta-type {@link ContractElement} and applies the AST transformations of the underlying
@@ -175,7 +175,7 @@ public class AnnotationProcessorVisitor extends BaseVisitor {
                     closureArgumentList.addExpression(varX(parameter));
                 }
 
-                if (methodNode.getReturnType() != ClassHelper.VOID_TYPE && isPostcondition && !(methodNode instanceof ConstructorNode)) {
+                if (!isPrimitiveVoid(methodNode.getReturnType()) && isPostcondition && !(methodNode instanceof ConstructorNode)) {
                     closureArgumentList.addExpression(localVarX("result", methodNode.getReturnType()));
                 }
 
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/classgen/asm/ContractClosureWriter.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/classgen/asm/ContractClosureWriter.java
index cd195c0..240ceb3 100644
--- a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/classgen/asm/ContractClosureWriter.java
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/classgen/asm/ContractClosureWriter.java
@@ -46,6 +46,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
 
@@ -68,7 +69,7 @@ public class ContractClosureWriter {
         removeParameter("old", parametersTemp);
         removeParameter("result", parametersTemp);
 
-        if (methodNode != null && addResultVariable && methodNode.getReturnType() != ClassHelper.VOID_TYPE) {
+        if (methodNode != null && addResultVariable && !isPrimitiveVoid(methodNode.getReturnType())) {
             parametersTemp.add(new Parameter(methodNode.getReturnType(), "result"));
         }
 
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/BaseGenerator.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/BaseGenerator.java
index 02bf376..0299db9 100644
--- a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/BaseGenerator.java
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/BaseGenerator.java
@@ -64,6 +64,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.notX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.tryCatchS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 
 /**
  * Base class for groovy-contracts code generators.
@@ -155,7 +156,7 @@ public abstract class BaseGenerator {
                 callArgumentList.addExpression(varX(parameter));
             }
 
-            if (isPostcondition && methodNode.getReturnType() != ClassHelper.VOID_TYPE && !(methodNode instanceof ConstructorNode)) {
+            if (isPostcondition && !isPrimitiveVoid(methodNode.getReturnType()) && !(methodNode instanceof ConstructorNode)) {
                 callArgumentList.addExpression(localVarX("result", methodNode.getReturnType()));
             }
 
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/ClassInvariantGenerator.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/ClassInvariantGenerator.java
index 5a6ccf5..7d41185 100644
--- a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/ClassInvariantGenerator.java
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/ClassInvariantGenerator.java
@@ -49,6 +49,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 
 /**
  * <p>
@@ -120,7 +121,7 @@ public class ClassInvariantGenerator extends BaseGenerator {
         Statement invariantMethodCall = stmt(callThisX(invariantMethod.getName()));
 
         final Statement statement = method.getCode();
-        if (statement instanceof BlockStatement && method.getReturnType() != ClassHelper.VOID_TYPE && !(method instanceof ConstructorNode)) {
+        if (statement instanceof BlockStatement && !isPrimitiveVoid(method.getReturnType()) && !(method instanceof ConstructorNode)) {
             final BlockStatement blockStatement = (BlockStatement) statement;
 
             final List<ReturnStatement> returnStatements = AssertStatementCreationUtility.getReturnStatements(method);
diff --git a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/PostconditionGenerator.java b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/PostconditionGenerator.java
index 0b39d82..5cbaf66 100644
--- a/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/PostconditionGenerator.java
+++ b/subprojects/groovy-contracts/src/main/java/org/apache/groovy/contracts/generation/PostconditionGenerator.java
@@ -45,6 +45,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveVoid;
 
 /**
  * <p>
@@ -124,7 +125,7 @@ public class PostconditionGenerator extends BaseGenerator {
         if (statements.size() > 0) {
             Expression contractsEnabled = localVarX(BaseVisitor.GCONTRACTS_ENABLED_VAR, ClassHelper.boolean_TYPE);
 
-            if (method.getReturnType() != ClassHelper.VOID_TYPE) {
+            if (!isPrimitiveVoid(method.getReturnType())) {
                 List<ReturnStatement> returnStatements = AssertStatementCreationUtility.getReturnStatements(method);
 
                 for (ReturnStatement returnStatement : returnStatements) {