Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 339248

Summary: [inline] Inline method adds wrong casts for Collections.fill(*)
Product: [Eclipse Project] JDT Reporter: Markus Keller <markus.kell.r>
Component: UIAssignee: Markus Keller <markus.kell.r>
Status: RESOLVED FIXED QA Contact:
Severity: normal    
Priority: P3    
Version: 3.7   
Target Milestone: 3.7 M7   
Hardware: All   
OS: All   
Whiteboard:
Attachments:
Description Flags
Workaround none

Description Markus Keller CLA 2011-03-08 11:44:57 EST
I20110307-2110

package xy;

import java.util.ArrayList;
import java.util.Collections;

public class C {
	void foo(ArrayList<String> al) {
		Collections.fill(al, "Hi");
	}
}

In 3.6, inlining the 'fill' method worked fine (except for the invisible FILL_THRESHOLD constant).

In HEAD, the result is this, with 2 unnecessary casts that contain the unavailable type variable T:

        int size = ((List<? super T>) al).size();
        
        if (size < Collections.FILL_THRESHOLD || al instanceof RandomAccess) {
            for (int i=0; i<size; i++)
                (al).set(i, (T) "Hi");
        } else {
            ListIterator<? super String> itr = (al).listIterator();
            for (int i=0; i<size; i++) {
                itr.next();
                itr.set("Hi");
            }
        }
Comment 1 Markus Keller CLA 2011-03-08 12:02:04 EST
Problem is not completely new (already present in 3.7 M4).

Deepak, can you please have a look?
Comment 2 Deepak Azad CLA 2011-03-08 12:29:36 EST
It is present in 3.7 M1 as well, most likely caused by bug 267386. Though in M1 the snippet from comment 0 results in 5 unnecessary casts :)
Comment 3 Deepak Azad CLA 2011-03-08 12:46:40 EST
Fix will probably go in ASTNodes.getExplicitCast(Expression, Expression) (which is called from SourceProvider.replaceParameterWithExpression(...)) or somewhere deep inside TypeRules.canAssign(ITypeBinding, ITypeBinding)...
Comment 4 Deepak Azad CLA 2011-03-08 13:07:12 EST
Simpler example. Inline fill1() and fill2()
----------------------------------------------------------------
package xy;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class C {
	void foo(ArrayList<String> al) {
		fill1(al);
		fill2("Hi");
	}

	public static <T> void fill1(List<? super T> list) {
		ListIterator<? super T> it = list.listIterator();
	}

	public static <T> void fill2(T object) {
		List<T> list = new ArrayList<T>();
		list.add(object);
	}
}
---------------------------------------------------------------
Comment 5 Markus Keller CLA 2011-03-08 17:02:48 EST
I guess the problem is that ASTNodes.getExplicitCast(*) doesn't have enough information. It is missing the actual type argument <String> for the type parameter <T> in the reference, so it can't know the actual type arguments to perform the analysis with.

I guess we have to take the MethodInvocation for the 'fill' method, resolve the method binding, and perform the substitutions to replace the type parameter (from methodBinding.getMethodDeclaration().getTypeParameters()) with the actual argument (from methodBinding.getTypeArguments()). The support for these substitutions will have to be added to the TypeEnvironment.
Comment 6 Markus Keller CLA 2011-03-15 11:34:32 EDT
As a first solution for 3.7, we can also just detect the problem and avoid the cast if the result contains type variables that are not defined in the context of the receiver.

That's much easier to implement, would not affect the other fixes we applied in 3.7, and would just bring us back into 3.6 state in the more exotic cases.
Comment 7 Markus Keller CLA 2011-04-19 09:14:24 EDT
Created attachment 193578 [details]
Workaround

We currently have a regression compared to 3.6. This patch avoids casts if the reference type contains type variables. A more complicated fix could go into 3.8 if this turns out to be an issue in practice.
Comment 8 Markus Keller CLA 2011-04-19 09:14:55 EDT
Fixed in HEAD.