Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forced to restate type argument boundaries #1786

Open
scabug opened this issue Mar 11, 2009 · 15 comments
Open

Forced to restate type argument boundaries #1786

scabug opened this issue Mar 11, 2009 · 15 comments

Comments

@scabug
Copy link

@scabug scabug commented Mar 11, 2009

The below code shows a compiler flaw in that the wildcard "_" as value for a bounded type parameter either breaks the bound - as it result in Any - or doesn't (as id hoped it to be) evaluates to the bound.

class SomeClass(val intValue:Int)
class MyClass[T <: SomeClass](val myValue:T) 

def myMethod(i:MyClass[_]) {
   i.myValue.intValue/2      // << error i.myValue is of type Any
}

def myMethod(i:MyClass[_ <: SomeClass]) {
   i.myValue.intValue/2      // << works
}
@scabug
Copy link
Author

@scabug scabug commented Mar 11, 2009

@scabug
Copy link
Author

@scabug scabug commented Mar 11, 2009

karlw said:
opps.. a comment correction, the line

val a = i.myValue.intValue/2 // << error i is of type Any

should be

val a = i.myValue.intValue/2 // << error i.myValue is of type Any

@scabug
Copy link
Author

@scabug scabug commented Oct 12, 2009

@odersky said:
This a consequence of the current type checking algorithm, where bounds
are checked only after variables are instantiated. I believe this will change once we go to contraint-based type inference. Assigning low priority until then.

@scabug
Copy link
Author

@scabug scabug commented Mar 28, 2012

@som-snytt said:
At 2.10.0-M2,

error: value intValue is not a member of _$1

At 2.9.1.final,

error: value intValue is not a member of Any

@scabug
Copy link
Author

@scabug scabug commented May 12, 2013

@scabug
Copy link
Author

@scabug scabug commented Feb 11, 2014

@adriaanm said (edited on Feb 11, 2014 7:56:58 PM UTC):
This will need some rethinking, as it caused several regressions in the wild. Slick and ScalaFX (#8197) both don't compile.

reopened by: scala/scala#3509

@scabug
Copy link
Author

@scabug scabug commented Feb 11, 2014

@adriaanm said:
The core of a likely more robust fix is in #6169, but we're not generalizing it for now.

@scabug
Copy link
Author

@scabug scabug commented Feb 11, 2014

Stephen Compall (s11001001) said (edited on Feb 11, 2014 8:27:54 PM UTC):
If reversion is going into 2.11, the news item that cites this issue for 2.11 should be removed. Or at least updated to point to #6169.

@scabug
Copy link
Author

@scabug scabug commented Feb 11, 2014

@scabug
Copy link
Author

@scabug scabug commented Dec 3, 2014

@retronym said (edited on Dec 3, 2014 11:45:54 AM UTC):
I've merged #8997 with this bug. It reports this test case:

object Test {
  type SerializableList[T <: Serializable] = List[T]
  class C(val sl: SerializableList[_])
  def unapply(x: C): Unit = Some.apply(x.sl) // type arguments [_$1] do not conform to type SerializableList's type parameter bounds [T <: Serializable]
}

@scabug
Copy link
Author

@scabug scabug commented Dec 3, 2014

Dmytro Kondratiuk (dk14) said (edited on Dec 3, 2014 12:43:39 PM UTC):
Maybe it's possible to authomatically add {code} _ <: Serializable {code} when generating constructor for case class.

@scabug
Copy link
Author

@scabug scabug commented Dec 3, 2014

@retronym said:
Not without initializing the symbol for the type constructor SerializableList. In general, doing so can trigger spurious cycles in typechecking. And trying to do it in cases when can can be sure we won't trigger a cycle can lead to unstable compilation results depending on order of files in a compilation batch. We have some more promising approaches.

@scabug
Copy link
Author

@scabug scabug commented Dec 3, 2014

Dmytro Kondratiuk (dk14) said (edited on Dec 3, 2014 1:15:49 PM UTC):
Oh, i see. But it may be helpful to add more user-friendly compilation error (at least with link to #1786) specially for case-classes (typecheck for unapply is triggired after SerializableList initialization). User may not think about x.sl call inside unapply method, so error is not so clear in this specific case. It can be achieved by analizing tree parameter inside scala.tools.nsc.typechecker.ContextErrors.NotWithinBounds method (or in better place), or it doesn't worth it?

@scabug
Copy link
Author

@scabug scabug commented Jan 12, 2015

@retronym said:
A test case from the mailing list, that involves interaction with #6169

import TestRecursiveTypes._

import language.existentials

object TestRecursiveTypesScala {
  
  def main(args: Array[String]): Unit = {
    val impl = new UpperImpl()
    
    impl.map().map().map()
   
    val upper = new TestRecursiveTypes().provideUpper()
    
    upper.map().map().map()
  }

  class Consumer extends UpperConsumer {
    def consume(upper: Upper[_]): Unit = {
      fbound(upper).map().map()
    }
  }
  // Workaround for the limbo state given that:
  // a) Java wildcard types propagate bounds (after the fix for https://issues.scala-lang.org/browse/SI-6169)
  // b) Scala existentials don't https://issues.scala-lang.org/browse/SI-1786
  def fbound(u: Upper[_]) = u.asInstanceOf[Upper[A] forSome { type A <: Upper[A] }]
}
public class TestRecursiveTypes
{
	// Base interface for F-Bounded polymorphism
	public interface Upper<T extends Upper<T>>
	{
		public T map();
	}
	
	// Implementation of F-Bounded polymorphism
	public static class UpperImpl implements Upper<UpperImpl>
	{
		@Override
		public UpperImpl map()
		{
			System.out.println(String.format("map@%s",this));
			return new UpperImpl();
		}
	}
	
	// Consumer interface
	public interface UpperConsumer
	{
		void consume(Upper<?> upper_);
	}
	
	// Consumer implementation
	public static class Consumer implements UpperConsumer
	{
		@Override
		public void consume(Upper<?> upper_)
		{
			upper_.map().map().map();
		}
	}
	
	// Upper factory method, returns interface
	public Upper<?> provideUpper() 
	{
		return new UpperImpl();
	}
	
	public static void main(String[] args)
	{
		new TestRecursiveTypes().run();
	}

	private void run()
	{
		Upper<?> upper = new UpperImpl();
		
		// This is also an upper, without conversio
		Upper<?> mapped = upper.map();
		
		Upper<?> upper2 = provideUpper();
	
		new Consumer().consume(upper2.map().map().map());
	}
}

@SethTisue
Copy link
Member

@SethTisue SethTisue commented Apr 24, 2019

see #11491 for some recent examples and discussion

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants