Bug 529685 - ECJ creates class with java.lang.ClassFormatError when
overriding method from Generic class
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedReferenceBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedReferenceBinding.java
index c3cf818..61ab7c6 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedReferenceBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedReferenceBinding.java
@@ -17,7 +17,13 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.compiler.lookup;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
+import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator;
 
 public class UnresolvedReferenceBinding extends ReferenceBinding {
 
@@ -174,4 +180,73 @@
 		return super.annotatedDebugName() + "(unresolved)"; //$NON-NLS-1$
 	return "Unresolved type " + ((this.compoundName != null) ? CharOperation.toString(this.compoundName) : "UNNAMED"); //$NON-NLS-1$ //$NON-NLS-2$
 }
+//{ObjectTeams: interning of deferred types awaiting possible wrapping as a role type:
+Map<WrapInfo,UnresolvedReferenceBinding> deferredWrappableTypes;
+/** Key for interning, capturing all context information from when a deferred type is being created. */
+static class WrapInfo {
+	Scope scope;
+	ReferenceBinding site;
+	ASTNode typedNode;
+	ProblemReporter originalReporter;
+	WrapInfo(Scope scope, ReferenceBinding site, ASTNode typedNode, ProblemReporter originalReporter) {
+		super();
+		this.scope = scope;
+		this.site = site;
+		this.typedNode = typedNode;
+		this.originalReporter = originalReporter;
+	}
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((this.originalReporter == null) ? 0 : this.originalReporter.hashCode());
+		result = prime * result + ((this.scope == null) ? 0 : this.scope.hashCode());
+		result = prime * result + ((this.site == null) ? 0 : this.site.hashCode());
+		result = prime * result + ((this.typedNode == null) ? 0 : this.typedNode.hashCode());
+		return result;
+	}
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		WrapInfo other = (WrapInfo) obj;
+		if (this.originalReporter != other.originalReporter)
+			return false;
+		if (this.scope != other.scope)
+			return false;
+		if (TypeBinding.notEquals(this.site, other.site))
+			return false;
+		if (this.typedNode != other.typedNode)
+			return false;
+		return true;
+	}
+}
+public TypeBinding deferredWrappableType(Scope scope, ReferenceBinding site, ASTNode typedNode, ProblemReporter originalReporter) {
+	WrapInfo key = new WrapInfo(scope, site, typedNode, originalReporter);
+	if (this.deferredWrappableTypes != null) {
+		UnresolvedReferenceBinding existing = this.deferredWrappableTypes.get(key);
+		if (existing != null)
+			return existing;
+	} else {
+		this.deferredWrappableTypes = new HashMap<>();
+	}
+	UnresolvedReferenceBinding deferred = new UnresolvedReferenceBinding(this.compoundName, getPackage()) {
+		@Override
+		public ReferenceBinding resolve(LookupEnvironment environment, boolean convertGenericToRawType) {
+			ReferenceBinding type = UnresolvedReferenceBinding.this.resolve(environment, convertGenericToRawType);
+			return (ReferenceBinding) RoleTypeCreator.maybeWrapUnqualifiedRoleType(scope, site, type, typedNode, originalReporter);
+		}
+		@Override public boolean hasTypeAnnotations() 			{ return UnresolvedReferenceBinding.this.hasTypeAnnotations(); }
+		@Override public boolean hasNullTypeAnnotations() 		{ return UnresolvedReferenceBinding.this.hasNullTypeAnnotations(); }
+		@Override public AnnotationBinding[] getAnnotations() 	{ return UnresolvedReferenceBinding.this.getAnnotations(); }
+	};
+	deferred.tagBits = this.tagBits;
+	this.deferredWrappableTypes.put(key, deferred);
+	return deferred;
+}
+// SH}
 }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/util/RoleTypeCreator.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/util/RoleTypeCreator.java
index 5cc1e4f..5920358 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/util/RoleTypeCreator.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/util/RoleTypeCreator.java
@@ -557,19 +557,7 @@
 	        
 	        if (typeToWrap instanceof UnresolvedReferenceBinding) {
 	        	// defer wrapping until resolve():
-	        	final UnresolvedReferenceBinding rawUnresolved = (UnresolvedReferenceBinding) typeToWrap;
-	        	final ProblemReporter originalReporter = problemReporter; 
-	        	return new UnresolvedReferenceBinding(rawUnresolved.compoundName, rawUnresolved.getPackage()) {
-	        		@Override
-	        		public ReferenceBinding resolve(LookupEnvironment environment, boolean convertGenericToRawType) {
-	        			ReferenceBinding type = rawUnresolved.resolve(environment, convertGenericToRawType);
-	        			return (ReferenceBinding) maybeWrapUnqualifiedRoleType(scope, site, type, typedNode, originalReporter);
-	        		}
-	        		@Override public boolean hasTypeAnnotations() 			{ return rawUnresolved.hasTypeAnnotations(); }
-	        		@Override public boolean hasNullTypeAnnotations() 		{ return rawUnresolved.hasNullTypeAnnotations(); }
-	        		@Override public AnnotationBinding[] getAnnotations() 	{ return rawUnresolved.getAnnotations(); }
-	        		{ this.tagBits = rawUnresolved.tagBits; }
-	        	};
+	        	return ((UnresolvedReferenceBinding) typeToWrap).deferredWrappableType(scope, site, typedNode, problemReporter);
 	        }
 	        if (typeToWrap instanceof IntersectionTypeBinding18) { // FIXME (recurse?)
 	        	return originalType;
@@ -1419,11 +1407,11 @@
 		for (int i = 0; i <= anchorArgPos; i++)
 			arguments[i].bind(scope, arguments[i].type.resolvedType, /*used*/i==anchorArgPos);
 		TeamAnchor anchor = arguments[anchorArgPos].binding; // SH: bounds check?
+		if (anchor == null)
+			return null;
 		((LocalVariableBinding)anchor).resolvedPosition = anchorArgPos;
 
 		// check anchor for error
-		if (anchor == null)
-			return null;
 	    if (   !anchor.isFinal()
 	    	&& !isConvertedArgument(anchor, scope))
 	    {
diff --git a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/regression/ReportedBugs.java b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/regression/ReportedBugs.java
index 87387c8..1a9530f 100644
--- a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/regression/ReportedBugs.java
+++ b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/regression/ReportedBugs.java
@@ -42,7 +42,7 @@
 	// Static initializer to specify tests subset using TESTS_* static variables
 	// All specified tests which does not belong to the class are skipped...
 	static {
-//		TESTS_NAMES = new String[] { "testBug433146"};
+		TESTS_NAMES = new String[] { "testBug529685"};
 //		TESTS_NUMBERS = new int[] { 1459 };
 //		TESTS_RANGE = new int[] { 1097, -1 };
 	}
@@ -5508,4 +5508,51 @@
 			"The type C1 is not generic; it cannot be parameterized with arguments <String>\n" + 
 			"----------\n");
     }
+    
+    // Bug 529685 - ECJ creates class with java.lang.ClassFormatError when overriding method from Generic class
+    public void testBug529685() {
+    	runConformTest(
+    		new String[] {
+    			"pack1/C.java",
+    			"package pack1;\n" +
+    			"public class C {}\n",
+    			"pack1/GC.java",
+    			"package pack1;\n" +
+    			"public abstract class GC<T extends pack1.C> {\n" +
+    			"	public void doit(T c) {}\n" +
+    			"}\n",
+    			"pack1/GAC.java",
+    			"package pack1;\n" +
+    			"public abstract class GAC<T extends pack1.C> extends GC<T> {\n" +
+    			"	public void doit(T c) {}\n" +
+    			"}\n",
+    			"pack1/GFC.java",
+    			"package pack1;\n" +
+    			"public abstract class GFC<T extends pack1.C> extends GAC<T> {\n" +
+    			"	public void doit(T c) {}\n" +
+    			"}\n",
+	    	});
+    	runConformTest(
+    		new String[] {
+				"pack1/AC.java",
+				"package pack1;\n" +
+				"public class AC extends ALFC {\n" +
+				"	public void doit(pack1.C c) {\n" +
+				"		System.out.print(\"OK\");" +
+				"	}\n" +
+				"	public static void main(String... args) {\n" +
+				"		pack1.GFC<pack1.C> p = new AC();\n" +
+				"		p.doit(new pack1.C());\n" +
+				"	}\n" +
+				"}\n",
+				"pack1/ALFC.java",
+				"package pack1;\n" +
+				"public class ALFC extends GFC<pack1.C> {\n" +
+				"}\n",
+    		},
+    		"OK",
+    		null/*libs*/,
+    		false/*shouldFlush*/,
+    		null/*vmArgs*/);
+    }
 }