summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbuild.xml4
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala211
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala39
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala22
-rw-r--r--test/files/neg/sammy_wrong_arity.check52
-rw-r--r--test/files/neg/sammy_wrong_arity.flags1
-rw-r--r--test/files/neg/sammy_wrong_arity.scala22
-rw-r--r--test/files/pos/sammy_poly.flags1
-rw-r--r--test/files/pos/sammy_poly.scala7
-rw-r--r--test/files/pos/sammy_scope.flags1
-rw-r--r--test/files/pos/sammy_scope.scala8
-rw-r--r--test/files/pos/sammy_single.flags1
-rw-r--r--test/files/pos/sammy_single.scala9
-rw-r--r--test/files/pos/sammy_twice.flags1
-rw-r--r--test/files/pos/sammy_twice.scala9
-rw-r--r--test/files/pos/t6221.scala8
-rw-r--r--test/files/run/sammy_java8.flags1
-rw-r--r--test/files/run/sammy_java8.scala34
-rw-r--r--test/files/run/t7398.scala2
20 files changed, 411 insertions, 27 deletions
diff --git a/build.xml b/build.xml
index ed9d02c4ce..cb159b6c9e 100755
--- a/build.xml
+++ b/build.xml
@@ -1625,7 +1625,7 @@ TODO:
</target>
<!-- See test/build-partest.xml for the macro(s) being used here. -->
- <target name="partest.task" depends="init,pack.done">
+ <target name="partest.task" depends="pack.done">
<!-- note the classpathref! this is the classpath used to run partest,
so it must have the new compiler.... -->
<taskdef
@@ -1633,7 +1633,7 @@ TODO:
resource="scala/tools/partest/antlib.xml"/>
</target>
- <target name="test.suite.init" depends="pack.done, partest.task">
+ <target name="test.suite.init" depends="partest.task">
<!-- read by test/partest to determine classpath used to run partest -->
<propertyfile file = "build/pack/partest.properties">
<entry key = "partest.classpath" value="${toString:partest.compilation.path}"/>
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index d97f62d5d6..ccf2266540 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -85,9 +85,6 @@ abstract class UnCurry extends InfoTransform
transformFunction(result)
}
- private lazy val serialVersionUIDAnnotation =
- AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List())
-
// I don't have a clue why I'm catching TypeErrors here, but it's better
// than spewing stack traces at end users for internal errors. Examples
// which hit at this point should not be hard to come by, but the immediate
@@ -220,7 +217,7 @@ abstract class UnCurry extends InfoTransform
case fun1 if fun1 ne fun => fun1
case _ =>
val parents = addSerializable(abstractFunctionForFunctionType(fun.tpe))
- val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation serialVersionUIDAnnotation
+ val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation SerialVersionUIDAnnotation
anonClass setInfo ClassInfoType(parents, newScope, anonClass)
val targs = fun.tpe.typeArgs
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 7bf342f475..de684e69ad 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -2503,7 +2503,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
assert(pt.typeSymbol == PartialFunctionClass, s"PartialFunction synthesis for match in $tree requires PartialFunction expected type, but got $pt.")
val targs = pt.dealiasWiden.typeArgs
- // if targs.head isn't fully defined, we can translate --> error
+ // if targs.head isn't fully defined, we can't translate --> error
targs match {
case argTp :: _ if isFullyDefined(argTp) => // ok
case _ => // uh-oh
@@ -2517,9 +2517,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// targs must conform to Any for us to synthesize an applyOrElse (fallback to apply otherwise -- typically for @cps annotated targs)
val targsValidParams = targs forall (_ <:< AnyTpe)
- val anonClass = (context.owner
- newAnonymousFunctionClass tree.pos
- addAnnotation AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List()))
+ val anonClass = context.owner newAnonymousFunctionClass tree.pos addAnnotation SerialVersionUIDAnnotation
import CODE._
@@ -2712,17 +2710,197 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
+ /** Synthesize and type check the implementation of a type with a Single Abstract Method
+ *
+ * `{ (p1: T1, ..., pN: TN) => body } : S`
+ *
+ * expands to (where `S` is the expected type that defines a single abstract method named `apply`)
+ *
+ * `{
+ * def apply$body(p1: T1, ..., pN: TN): T = body
+ * new S {
+ * def apply(p1: T1, ..., pN: TN): T = apply$body(p1,..., pN)
+ * }
+ * }`
+ *
+ * If 'T' is not fully defined, it is inferred by type checking
+ * `apply$body` without a result type before type checking the block.
+ * The method's inferred result type is used instead of T`. [See test/files/pos/sammy_poly.scala]
+ *
+ * The `apply` method is identified by the argument `sam`; `S` corresponds to the argument `samClassTp`,
+ * and `resPt` is derived from `samClassTp` -- it may be fully defined, or not...
+ *
+ * The function's body is put in a method outside of the class definition to enforce scoping.
+ * S's members should not be in scope in `body`.
+ *
+ * The restriction on implicit arguments (neither S's constructor, nor sam may take an implicit argument list),
+ * is largely to keep the implementation of type inference (the computation of `samClassTpFullyDefined`) simple.
+ *
+ * NOTE: it would be nicer to not have to type check `apply$body` separately when `T` is not fully defined.
+ * However T must be fully defined before we type the instantiation, as it'll end up as a parent type,
+ * which must be fully defined. Would be nice to have some kind of mechanism to insert type vars in a block of code,
+ * and have the instantiation of the first occurrence propagate to the rest of the block.
+ */
+ def synthesizeSAMFunction(sam: Symbol, fun: Function, resPt: Type, samClassTp: Type, mode: Mode): Tree = {
+ // assert(fun.vparams forall (vp => isFullyDefined(vp.tpt.tpe))) -- by construction, as we take them from sam's info
+ val sampos = fun.pos
+
+ // if the expected sam type is fully defined, use it for the method's result type
+ // otherwise, NoType, so that type inference will determine the method's result type
+ // resPt is syntactically contained in samClassTp, so if the latter is fully defined, so is the former
+ // ultimately, we want to fully define samClassTp as it is used as the superclass of our anonymous class
+ val samDefTp = if (isFullyDefined(resPt)) resPt else NoType
+ val bodyName = newTermName(sam.name + "$body")
+
+ // `def '${sam.name}\$body'($p1: $T1, ..., $pN: $TN): $resPt = $body`
+ val samBodyDef =
+ DefDef(NoMods,
+ bodyName,
+ Nil,
+ List(fun.vparams.map(_.duplicate)), // must duplicate as we're also using them for `samDef`
+ TypeTree(samDefTp) setPos sampos.focus,
+ fun.body)
+
+ // If we need to enter the sym for the body def before type checking the block,
+ // we'll create a nested context, as explained below.
+ var nestedTyper = this
+
+ // Type check body def before classdef to fully determine samClassTp (if necessary).
+ // As `samClassTp` determines a parent type for the class,
+ // we can't type check `block` in one go unless `samClassTp` is fully defined.
+ val samClassTpFullyDefined =
+ if (isFullyDefined(samClassTp)) samClassTp
+ else try {
+ // This creates a symbol for samBodyDef with a type completer that'll be triggered immediately below.
+ // The symbol is entered in the same scope used for the block below, and won't thus be reentered later.
+ // It has to be a new scope, though, or we'll "get ambiguous reference to overloaded definition" [pos/sammy_twice.scala]
+ // makeSilent: [pos/nonlocal-unchecked.scala -- when translation all functions to sams]
+ val nestedCtx = enterSym(context.makeNewScope(context.tree, context.owner).makeSilent(), samBodyDef)
+ nestedTyper = newTyper(nestedCtx)
+
+ // NOTE: this `samBodyDef.symbol.info` runs the type completer set up by the enterSym above
+ val actualSamType = samBodyDef.symbol.info
+
+ // we're trying to fully define the type arguments for this type constructor
+ val samTyCon = samClassTp.typeSymbol.typeConstructor
+
+ // the unknowns
+ val tparams = samClassTp.typeSymbol.typeParams
+ // ... as typevars
+ val tvars = tparams map freshVar
+
+ // 1. Recover partial information:
+ // - derive a type from samClassTp that has the corresponding tparams for type arguments that aren't fully defined
+ // - constrain typevars to be equal to type args that are fully defined
+ val samClassTpMoreDefined = appliedType(samTyCon,
+ (samClassTp.typeArgs, tparams, tvars).zipped map {
+ case (a, _, tv) if isFullyDefined(a) => tv =:= a; a
+ case (_, p, _) => p.typeConstructor
+ })
+
+ // the method type we're expecting the synthesized sam to have, based on the expected sam type,
+ // where fully defined type args to samClassTp have been preserved,
+ // with the unknown args replaced by their corresponding type param
+ val expectedSamType = samClassTpMoreDefined.memberInfo(sam)
+
+ // 2. make sure the body def's actual type (formals and result) conforms to
+ // sam's expected type (in terms of the typevars that represent the sam's class's type params)
+ actualSamType <:< expectedSamType.substituteTypes(tparams, tvars)
+
+ // solve constraints tracked by tvars
+ val targs = solvedTypes(tvars, tparams, tparams map varianceInType(sam.info), upper = false, lubDepth(sam.info :: Nil))
+
+ debuglog(s"sam infer: $samClassTp --> ${appliedType(samTyCon, targs)} by $actualSamType <:< $expectedSamType --> $targs for $tparams")
+
+ // a fully defined samClassTp
+ appliedType(samTyCon, targs)
+ } catch {
+ case _: NoInstance | _: TypeError =>
+ devWarning(sampos, s"Could not define type $samClassTp using ${samBodyDef.symbol.rawInfo} <:< ${samClassTp memberInfo sam} (for $sam)")
+ samClassTp
+ }
+
+ // `final override def ${sam.name}($p1: $T1, ..., $pN: $TN): $resPt = ${sam.name}\$body'($p1, ..., $pN)`
+ val samDef =
+ DefDef(Modifiers(FINAL | OVERRIDE | SYNTHETIC),
+ sam.name.toTermName,
+ Nil,
+ List(fun.vparams),
+ TypeTree(samBodyDef.tpt.tpe) setPos sampos.focus,
+ Apply(Ident(bodyName), fun.vparams map (p => Ident(p.name)))
+ )
+
+ val serializableParentAddendum =
+ if (typeIsSubTypeOfSerializable(samClassTp)) Nil
+ else List(TypeTree(SerializableTpe))
+
+ val classDef =
+ ClassDef(Modifiers(FINAL), tpnme.ANON_FUN_NAME, tparams = Nil,
+ gen.mkTemplate(
+ parents = TypeTree(samClassTpFullyDefined) :: serializableParentAddendum,
+ self = emptyValDef,
+ constrMods = NoMods,
+ vparamss = ListOfNil,
+ body = List(samDef),
+ superPos = sampos.focus
+ )
+ )
+
+ // type checking the whole block, so that everything is packaged together nicely
+ // and we don't have to create any symbols by hand
+ val block =
+ nestedTyper.typedPos(sampos, mode, samClassTpFullyDefined) {
+ Block(
+ samBodyDef,
+ classDef,
+ Apply(Select(New(Ident(tpnme.ANON_FUN_NAME)), nme.CONSTRUCTOR), Nil)
+ )
+ }
+
+ classDef.symbol addAnnotation SerialVersionUIDAnnotation
+ block
+ }
+
+ /** Type check a function literal.
+ *
+ * Based on the expected type pt, potentially synthesize an instance of
+ * - PartialFunction,
+ * - a type with a Single Abstract Method (under -Xexperimental for now).
+ */
private def typedFunction(fun: Function, mode: Mode, pt: Type): Tree = {
val numVparams = fun.vparams.length
- if (numVparams > definitions.MaxFunctionArity)
- return MaxFunctionArityError(fun)
+ val FunctionSymbol =
+ if (numVparams > definitions.MaxFunctionArity) NoSymbol
+ else FunctionClass(numVparams)
- val FunctionSymbol = FunctionClass(numVparams)
- val (argpts, respt) = pt baseType FunctionSymbol match {
- case TypeRef(_, FunctionSymbol, args :+ res) => (args, res)
- case _ => (fun.vparams map (_ => if (pt == ErrorType) ErrorType else NoType), WildcardType)
- }
- if (argpts.lengthCompare(numVparams) != 0)
+ /* The Single Abstract Member of pt, unless pt is the built-in function type of the expected arity,
+ * as `(a => a): Int => Int` should not (yet) get the sam treatment.
+ */
+ val sam =
+ if (!settings.Xexperimental || pt.typeSymbol == FunctionSymbol) NoSymbol
+ else samOf(pt)
+
+ /* The SAM case comes first so that this works:
+ * abstract class MyFun extends (Int => Int)
+ * (a => a): MyFun
+ *
+ * Note that the arity of the sam must correspond to the arity of the function.
+ */
+ val samViable = sam.exists && sameLength(sam.info.params, fun.vparams)
+ val (argpts, respt) =
+ if (samViable) {
+ val samInfo = pt memberInfo sam
+ (samInfo.paramTypes, samInfo.resultType)
+ } else {
+ pt baseType FunctionSymbol match {
+ case TypeRef(_, FunctionSymbol, args :+ res) => (args, res)
+ case _ => (fun.vparams map (_ => if (pt == ErrorType) ErrorType else NoType), WildcardType)
+ }
+ }
+
+ if (!FunctionSymbol.exists)
+ MaxFunctionArityError(fun)
+ else if (argpts.lengthCompare(numVparams) != 0)
WrongNumberOfParametersError(fun, argpts)
else {
foreach2(fun.vparams, argpts) { (vparam, argpt) =>
@@ -2733,7 +2911,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
fun match {
case etaExpansion(vparams, fn, args) =>
silent(_.typed(fn, mode.forFunMode, pt)) filter (_ => context.undetparams.isEmpty) map { fn1 =>
- // if context,undetparams is not empty, the function was polymorphic,
+ // if context.undetparams is not empty, the function was polymorphic,
// so we need the missing arguments to infer its type. See #871
//println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams)
val ftpe = normalize(fn1.tpe) baseType FunctionClass(numVparams)
@@ -2761,6 +2939,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (p.tpt.tpe == null) p.tpt setType outerTyper.typedType(p.tpt).tpe
outerTyper.synthesizePartialFunction(p.name, p.pos, fun.body, mode, pt)
+
+ // Use synthesizeSAMFunction to expand `(p1: T1, ..., pN: TN) => body`
+ // to an instance of the corresponding anonymous subclass of `pt`.
+ case _ if samViable =>
+ newTyper(context.outer).synthesizeSAMFunction(sam, fun, respt, pt, mode)
+
+ // regular Function
case _ =>
val vparamSyms = fun.vparams map { vparam =>
enterSym(context, vparam)
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 4d8ae73bcc..99aad4f057 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -799,6 +799,44 @@ trait Definitions extends api.StandardDefinitions {
(sym eq PartialFunctionClass) || (sym eq AbstractPartialFunctionClass)
}
+ /** The single abstract method declared by type `tp` (or `NoSymbol` if it cannot be found).
+ *
+ * The method must be monomorphic and have exactly one parameter list.
+ * The class defining the method is a supertype of `tp` that
+ * has a public no-arg primary constructor.
+ */
+ def samOf(tp: Type): Symbol = {
+ // if tp has a constructor, it must be public and must not take any arguments
+ // (not even an implicit argument list -- to keep it simple for now)
+ val tpSym = tp.typeSymbol
+ val ctor = tpSym.primaryConstructor
+ val ctorOk = !ctor.exists || (!ctor.isOverloaded && ctor.isPublic && ctor.info.params.isEmpty && ctor.info.paramSectionCount <= 1)
+
+ if (tpSym.exists && ctorOk) {
+ // find the single abstract member, if there is one
+ // don't go out requiring DEFERRED members, as you will get them even if there's a concrete override:
+ // scala> abstract class X { def m: Int }
+ // scala> class Y extends X { def m: Int = 1}
+ // scala> typeOf[Y].deferredMembers
+ // Scopes(method m, method getClass)
+ //
+ // scala> typeOf[Y].members.filter(_.isDeferred)
+ // Scopes()
+ // must filter out "universal" members (getClass is deferred for some reason)
+ val deferredMembers = (
+ tp membersBasedOnFlags (excludedFlags = BridgeAndPrivateFlags, requiredFlags = METHOD)
+ filter (mem => mem.isDeferredNotDefault && !isUniversalMember(mem)) // TODO: test
+ )
+
+ // if there is only one, it's monomorphic and has a single argument list
+ if (deferredMembers.size == 1 &&
+ deferredMembers.head.typeParams.isEmpty &&
+ deferredMembers.head.info.paramSectionCount == 1)
+ deferredMembers.head
+ else NoSymbol
+ } else NoSymbol
+ }
+
def arrayType(arg: Type) = appliedType(ArrayClass, arg)
def byNameType(arg: Type) = appliedType(ByNameParamClass, arg)
def iteratorOfType(tp: Type) = appliedType(IteratorClass, tp)
@@ -1089,6 +1127,7 @@ trait Definitions extends api.StandardDefinitions {
lazy val ScalaInlineClass = requiredClass[scala.inline]
lazy val ScalaNoInlineClass = requiredClass[scala.noinline]
lazy val SerialVersionUIDAttr = requiredClass[scala.SerialVersionUID]
+ lazy val SerialVersionUIDAnnotation = AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List())
lazy val SpecializedClass = requiredClass[scala.specialized]
lazy val ThrowsClass = requiredClass[scala.throws[_]]
lazy val TransientAttr = requiredClass[scala.transient]
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index 85dfa037ec..8f19e89dd4 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -974,6 +974,18 @@ trait Types
else (baseClasses.head.newOverloaded(this, alts))
}
+ /** Find all members meeting the flag requirements.
+ *
+ * If you require a DEFERRED member, you will get it if it exists -- even if there's an overriding concrete member.
+ * If you exclude DEFERRED members, or don't specify any requirements,
+ * you won't get deferred members (whether they have an overriding concrete member or not)
+ *
+ * Thus, findMember requiring DEFERRED flags yields deferred members,
+ * while `findMember(excludedFlags = 0, requiredFlags = 0).filter(_.isDeferred)` may not (if there's a corresponding concrete member)
+ *
+ * Requirements take precedence over exclusions, so requiring and excluding DEFERRED will yield a DEFERRED member (if there is one).
+ *
+ */
def findMembers(excludedFlags: Long, requiredFlags: Long): Scope = {
def findMembersInternal: Scope = {
var members: Scope = null
@@ -983,10 +995,10 @@ trait Types
//Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG
var required = requiredFlags
var excluded = excludedFlags | DEFERRED
- var continue = true
+ var retryForDeferred = true
var self: Type = null
- while (continue) {
- continue = false
+ while (retryForDeferred) {
+ retryForDeferred = false
val bcs0 = baseClasses
var bcs = bcs0
while (!bcs.isEmpty) {
@@ -1018,7 +1030,7 @@ trait Types
}
if (others eq null) members enter sym
} else if (excl == DEFERRED) {
- continue = true
+ retryForDeferred = (excludedFlags & DEFERRED) == 0
}
}
entry = entry.next
@@ -1028,7 +1040,7 @@ trait Types
} // while (!bcs.isEmpty)
required |= DEFERRED
excluded &= ~(DEFERRED.toLong)
- } // while (continue)
+ } // while (retryForDeferred)
if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start)
if (members eq null) EmptyScope else members
}
diff --git a/test/files/neg/sammy_wrong_arity.check b/test/files/neg/sammy_wrong_arity.check
new file mode 100644
index 0000000000..af547a201d
--- /dev/null
+++ b/test/files/neg/sammy_wrong_arity.check
@@ -0,0 +1,52 @@
+sammy_wrong_arity.scala:6: error: type mismatch;
+ found : () => Int
+ required: T1
+ (() => 0): T1
+ ^
+sammy_wrong_arity.scala:7: error: type mismatch;
+ found : Any => Int
+ required: T2
+ ((x: Any) => 0): T2
+ ^
+sammy_wrong_arity.scala:9: error: type mismatch;
+ found : Any => Int
+ required: T0
+ ((x: Any) => 0): T0
+ ^
+sammy_wrong_arity.scala:10: error: type mismatch;
+ found : Any => Int
+ required: T2
+ ((x: Any) => 0): T2
+ ^
+sammy_wrong_arity.scala:12: error: type mismatch;
+ found : (Any, Any) => Int
+ required: T0
+ ((x: Any, y: Any) => 0): T0
+ ^
+sammy_wrong_arity.scala:13: error: type mismatch;
+ found : (Any, Any) => Int
+ required: T1
+ ((x: Any, y: Any) => 0): T1
+ ^
+sammy_wrong_arity.scala:15: error: missing parameter type
+ ((x) => 0): T2
+ ^
+sammy_wrong_arity.scala:17: error: missing parameter type
+ ((x) => 0): T0
+ ^
+sammy_wrong_arity.scala:18: error: missing parameter type
+ ((x) => 0): T2
+ ^
+sammy_wrong_arity.scala:20: error: missing parameter type
+ ((x, y) => 0): T0
+ ^
+sammy_wrong_arity.scala:20: error: missing parameter type
+ ((x, y) => 0): T0
+ ^
+sammy_wrong_arity.scala:21: error: missing parameter type
+ ((x, y) => 0): T1
+ ^
+sammy_wrong_arity.scala:21: error: missing parameter type
+ ((x, y) => 0): T1
+ ^
+13 errors found
diff --git a/test/files/neg/sammy_wrong_arity.flags b/test/files/neg/sammy_wrong_arity.flags
new file mode 100644
index 0000000000..48fd867160
--- /dev/null
+++ b/test/files/neg/sammy_wrong_arity.flags
@@ -0,0 +1 @@
+-Xexperimental
diff --git a/test/files/neg/sammy_wrong_arity.scala b/test/files/neg/sammy_wrong_arity.scala
new file mode 100644
index 0000000000..d03d266a0b
--- /dev/null
+++ b/test/files/neg/sammy_wrong_arity.scala
@@ -0,0 +1,22 @@
+trait T0 { def ap(): Int }
+trait T1 { def ap(a: Any): Int }
+trait T2 { def ap(a: Any, b: Any): Int }
+
+class Test {
+ (() => 0): T1
+ ((x: Any) => 0): T2
+
+ ((x: Any) => 0): T0
+ ((x: Any) => 0): T2
+
+ ((x: Any, y: Any) => 0): T0
+ ((x: Any, y: Any) => 0): T1
+
+ ((x) => 0): T2
+
+ ((x) => 0): T0
+ ((x) => 0): T2
+
+ ((x, y) => 0): T0
+ ((x, y) => 0): T1
+}
diff --git a/test/files/pos/sammy_poly.flags b/test/files/pos/sammy_poly.flags
new file mode 100644
index 0000000000..48fd867160
--- /dev/null
+++ b/test/files/pos/sammy_poly.flags
@@ -0,0 +1 @@
+-Xexperimental
diff --git a/test/files/pos/sammy_poly.scala b/test/files/pos/sammy_poly.scala
new file mode 100644
index 0000000000..c629be7166
--- /dev/null
+++ b/test/files/pos/sammy_poly.scala
@@ -0,0 +1,7 @@
+// test synthesizeSAMFunction where the sam type is not fully defined
+class T {
+ trait F[T, U] { def apply(x: T): U }
+ // NOTE: the f(x) desugaring for now assumes the single abstract method is called 'apply'
+ def app[T, U](x: T)(f: F[T, U]): U = f(x)
+ app(1)(x => List(x))
+} \ No newline at end of file
diff --git a/test/files/pos/sammy_scope.flags b/test/files/pos/sammy_scope.flags
new file mode 100644
index 0000000000..48fd867160
--- /dev/null
+++ b/test/files/pos/sammy_scope.flags
@@ -0,0 +1 @@
+-Xexperimental
diff --git a/test/files/pos/sammy_scope.scala b/test/files/pos/sammy_scope.scala
new file mode 100644
index 0000000000..8f1fe7058e
--- /dev/null
+++ b/test/files/pos/sammy_scope.scala
@@ -0,0 +1,8 @@
+// test synthesizeSAMFunction: scope hygiene
+abstract class SamFun[T1, R] { self =>
+ def apply(v1: T1): R
+
+ // this should type check, as the apply ref is equivalent to self.apply
+ // it shouldn't resolve to the sam's apply that's synthesized (that wouldn't type check, hence the pos test)
+ def compose[A](g: SamFun[A, T1]): SamFun[A, R] = { x => apply(g(x)) }
+} \ No newline at end of file
diff --git a/test/files/pos/sammy_single.flags b/test/files/pos/sammy_single.flags
new file mode 100644
index 0000000000..48fd867160
--- /dev/null
+++ b/test/files/pos/sammy_single.flags
@@ -0,0 +1 @@
+-Xexperimental
diff --git a/test/files/pos/sammy_single.scala b/test/files/pos/sammy_single.scala
new file mode 100644
index 0000000000..7a3d272983
--- /dev/null
+++ b/test/files/pos/sammy_single.scala
@@ -0,0 +1,9 @@
+// test that dependent types work
+// TODO: def apply(x: String): x.type does NOT work yet
+object Test {
+ val s: String = ""
+
+ trait T { def apply(x: s.type): s.type }
+
+ val preservedResult: s.type = ((x => x): T)(s)
+} \ No newline at end of file
diff --git a/test/files/pos/sammy_twice.flags b/test/files/pos/sammy_twice.flags
new file mode 100644
index 0000000000..48fd867160
--- /dev/null
+++ b/test/files/pos/sammy_twice.flags
@@ -0,0 +1 @@
+-Xexperimental
diff --git a/test/files/pos/sammy_twice.scala b/test/files/pos/sammy_twice.scala
new file mode 100644
index 0000000000..c91f5b9fd2
--- /dev/null
+++ b/test/files/pos/sammy_twice.scala
@@ -0,0 +1,9 @@
+// test repeated synthesizeSAMFunction where the sam type is not fully defined
+// the naive implementation would enter the same apply$body in the same scope twice
+trait F[T, U] { def apply(x: T): U }
+
+class C {
+ def app[T, U](x: T)(f: F[T, U]): U = f(x)
+ app(1)(x => List(x))
+ app(2)(x => List(x))
+} \ No newline at end of file
diff --git a/test/files/pos/t6221.scala b/test/files/pos/t6221.scala
index dd7776f596..34f02859f3 100644
--- a/test/files/pos/t6221.scala
+++ b/test/files/pos/t6221.scala
@@ -7,23 +7,27 @@ class MyCollection[A] {
class OtherFunc[-A, +B] {}
object Test {
- implicit def functionToMyFunc[A, B](f: A => B): MyFunc[A, B] = new MyFunc
+ implicit def functionToMyFunc[A, B](f: A => B): MyFunc[A, B] = new MyFunc // = new MyFunc[A,Nothing]();
- implicit def otherFuncToMyFunc[A, B](f: OtherFunc[A, B]): MyFunc[A, B] = new MyFunc
+ implicit def otherFuncToMyFunc[A, B](f: OtherFunc[A, B]): MyFunc[A, B] = new MyFunc // = new MyFunc[A,Nothing]();
def main(args: Array[String]) {
val col = new MyCollection[Int]
// Doesn't compile: error: missing parameter type for expanded function ((x$1) => x$1.toString)
println(col.map(_.toString))
+ // scala.this.Predef.println(col.map[String](Test.this.functionToMyFunc[Int, String](((x$1: Int) => x$1.toString()))));
// Doesn't compile: error: missing parameter type
println(col.map(x => x.toString))
+ // scala.this.Predef.println(col.map[String](Test.this.functionToMyFunc[Int, String](((x: Int) => x.toString()))));
// Does compile
println(col.map((x: Int) => x.toString))
+ // scala.this.Predef.println(col.map[String](Test.this.functionToMyFunc[Int, String](((x: Int) => x.toString()))));
// Does compile (even though type params of OtherFunc not given)
println(col.map(new OtherFunc))
+ // scala.this.Predef.println(col.map[Nothing](Test.this.otherFuncToMyFunc[Any, Nothing](new OtherFunc[Any,Nothing]())))
}
} \ No newline at end of file
diff --git a/test/files/run/sammy_java8.flags b/test/files/run/sammy_java8.flags
new file mode 100644
index 0000000000..48fd867160
--- /dev/null
+++ b/test/files/run/sammy_java8.flags
@@ -0,0 +1 @@
+-Xexperimental
diff --git a/test/files/run/sammy_java8.scala b/test/files/run/sammy_java8.scala
new file mode 100644
index 0000000000..db9df7f5fe
--- /dev/null
+++ b/test/files/run/sammy_java8.scala
@@ -0,0 +1,34 @@
+import scala.tools.partest._
+
+// java8 version of sammy_poly.scala
+object Test extends CompilerTest {
+ import global._
+
+ override lazy val units: List[CompilationUnit] = {
+ global.settings.Xexperimental.value = true
+
+ // This test itself does not depend on JDK8.
+ javaCompilationUnits(global)(samSource) ++
+ compilationUnits(global)(useSamSource)
+ }
+
+ private def samSource = """
+// trait F[T, U] { def apply(x: T): U }
+public interface F<T, U> {
+ U apply(T t);
+ default void yadayada() {
+ throw new UnsupportedOperationException("yadayada");
+ }
+}
+ """
+
+ private def useSamSource = """
+class T {
+ def app[T, U](x: T)(f: F[T, U]): U = f(x)
+ app(1)(x => List(x))
+}
+ """
+
+ // We're only checking we can compile it.
+ def check(source: String, unit: global.CompilationUnit): Unit = ()
+}
diff --git a/test/files/run/t7398.scala b/test/files/run/t7398.scala
index 493c4dcf40..4b46850768 100644
--- a/test/files/run/t7398.scala
+++ b/test/files/run/t7398.scala
@@ -21,6 +21,6 @@ public interface Iterator<E> {
}
"""
- // We're only checking we can parse it.
+ // We're only checking we can compile it.
def check(source: String, unit: global.CompilationUnit): Unit = ()
}