From 373f22a2022519ab894c1ea77460e6460d7c2ee4 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Mon, 27 Aug 2012 16:03:59 +0200 Subject: Fix SI-4581. Specifically, the final flag on the generated static field is no longer ommitted. Fix 2 failing test-cases. --- test/files/neg/static-annot.check | 5 ++++- test/files/neg/t4581.check | 0 test/files/neg/t4581/static-declaration_1.scala | 14 ++++++++++++++ test/files/neg/t4581/static_2.java | 15 +++++++++++++++ test/files/run/static-annot-repl.check | 12 +++++++++--- 5 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 test/files/neg/t4581.check create mode 100644 test/files/neg/t4581/static-declaration_1.scala create mode 100644 test/files/neg/t4581/static_2.java (limited to 'test/files') diff --git a/test/files/neg/static-annot.check b/test/files/neg/static-annot.check index 66efebdcee..c98e7d9658 100644 --- a/test/files/neg/static-annot.check +++ b/test/files/neg/static-annot.check @@ -13,7 +13,10 @@ static-annot.scala:38: error: The @static annotation is only allowed on public m static-annot.scala:39: error: The @static annotation is not allowed on lazy members. @static lazy val bam = 3 ^ +static-annot.scala:52: error: The @static annotation is not allowed on method definitions. + @static def x = 42 + ^ static-annot.scala:14: error: Only members of top-level objects and their nested objects can be annotated with @static. @static val blah = 2 ^ -6 errors found \ No newline at end of file +7 errors found diff --git a/test/files/neg/t4581.check b/test/files/neg/t4581.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/files/neg/t4581/static-declaration_1.scala b/test/files/neg/t4581/static-declaration_1.scala new file mode 100644 index 0000000000..f9a66b29c1 --- /dev/null +++ b/test/files/neg/t4581/static-declaration_1.scala @@ -0,0 +1,14 @@ + + + + + +object Constants { + import scala.annotation.static + @static val Const: Int = 0 // should generate a static final field + @static final val FinalConst: Int = 0 // ditto + @static var MutableField: Int = 0 // should not be final +} + + + diff --git a/test/files/neg/t4581/static_2.java b/test/files/neg/t4581/static_2.java new file mode 100644 index 0000000000..2fd5bf1d82 --- /dev/null +++ b/test/files/neg/t4581/static_2.java @@ -0,0 +1,15 @@ + + + + +public class static_2 { + public static void main(String[] args) { + Constants.Const = 17; + Constants.FinalConst = 99; + Constants.MutableField = 199; + } +} + + + + diff --git a/test/files/run/static-annot-repl.check b/test/files/run/static-annot-repl.check index d1029a9809..3a1532b823 100644 --- a/test/files/run/static-annot-repl.check +++ b/test/files/run/static-annot-repl.check @@ -13,7 +13,9 @@ scala> @static val x2 = 43 x2: Int = 43 scala> @static def x3 = 44 -x3: Int +:8: error: The @static annotation is not allowed on method definitions. + @static def x3 = 44 + ^ scala> x1 res0: Int = 42 @@ -22,11 +24,15 @@ scala> x2 res1: Int = 43 scala> x3 -res2: Int = 44 +:9: error: not found: value x3 + x3 + ^ scala> class Test { @static def x = 42 } -defined class Test +:9: error: The @static annotation is not allowed on method definitions. + @static def x = 42 + ^ scala> \ No newline at end of file -- cgit v1.2.3 From 23cf705ba64641a3ba45e84984702cf97cdd0ccf Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Mon, 27 Aug 2012 18:08:19 +0200 Subject: definitive way to learn if a symbol is a val/var I think `isVal` and `isVar` are the right names, because they exactly map on Scala syntax. --- src/reflect/scala/reflect/api/Symbols.scala | 8 ++++---- src/reflect/scala/reflect/internal/Symbols.scala | 2 ++ test/files/run/reflection-fieldmirror-accessorsareokay.scala | 2 +- test/files/run/reflection-fieldmirror-nmelocalsuffixstring.scala | 2 +- test/files/run/reflection-fieldmirror-privatethis.scala | 2 +- test/files/run/reflection-fieldsymbol-navigation.scala | 4 ++-- 6 files changed, 11 insertions(+), 9 deletions(-) (limited to 'test/files') diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala index 2673069eef..8617ae975d 100644 --- a/src/reflect/scala/reflect/api/Symbols.scala +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -212,16 +212,16 @@ trait Symbols extends base.Symbols { self: Universe => /** The API of term symbols */ trait TermSymbolApi extends SymbolApi with TermSymbolBase { this: TermSymbol => - /** Does this symbol represent a value, i.e. not a module and not a method? + /** Is this symbol introduced as `val`? */ - def isValue: Boolean + def isVal: Boolean /** Does this symbol denote a stable value? */ def isStable: Boolean - /** Does this symbol represent a mutable value? + /** Is this symbol introduced as `var`? */ - def isVariable: Boolean + def isVar: Boolean /** Does this symbol represent a getter or a setter? */ diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 07614361c5..4d60566474 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -70,6 +70,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isByNameParam: Boolean = this.isValueParameter && (this hasFlag BYNAMEPARAM) def isImplementationArtifact: Boolean = (this hasFlag BRIDGE) || (this hasFlag VBRIDGE) || (this hasFlag ARTIFACT) def isJava: Boolean = this hasFlag JAVA + def isVal: Boolean = isTerm && !isModule && !isMethod && !isMutable + def isVar: Boolean = isTerm && !isModule && !isMethod && isMutable def newNestedSymbol(name: Name, pos: Position, newFlags: Long, isClass: Boolean): Symbol = name match { case n: TermName => newTermSymbol(n, pos, newFlags) diff --git a/test/files/run/reflection-fieldmirror-accessorsareokay.scala b/test/files/run/reflection-fieldmirror-accessorsareokay.scala index 9590cbe811..16354025f3 100644 --- a/test/files/run/reflection-fieldmirror-accessorsareokay.scala +++ b/test/files/run/reflection-fieldmirror-accessorsareokay.scala @@ -14,7 +14,7 @@ object Test extends App { def test(f: Symbol) = { try { val fm: FieldMirror = im.reflectField(f.asTerm) - println(fm.symbol.isVariable) + println(fm.symbol.isVar) println(fm.get) fm.set(2) println(fm.get) diff --git a/test/files/run/reflection-fieldmirror-nmelocalsuffixstring.scala b/test/files/run/reflection-fieldmirror-nmelocalsuffixstring.scala index 5cfe583ed5..2b4a9bb55e 100644 --- a/test/files/run/reflection-fieldmirror-nmelocalsuffixstring.scala +++ b/test/files/run/reflection-fieldmirror-nmelocalsuffixstring.scala @@ -12,5 +12,5 @@ object Test extends App { val cs = im.symbol val f = cs.typeSignature.declaration(newTermName("x" + nme.LOCAL_SUFFIX_STRING)).asTerm val fm: FieldMirror = im.reflectField(f) - println(fm.symbol.isVariable) + println(fm.symbol.isVar) } diff --git a/test/files/run/reflection-fieldmirror-privatethis.scala b/test/files/run/reflection-fieldmirror-privatethis.scala index 7aa179958d..ab838dbb1b 100644 --- a/test/files/run/reflection-fieldmirror-privatethis.scala +++ b/test/files/run/reflection-fieldmirror-privatethis.scala @@ -12,7 +12,7 @@ object Test extends App { val cs = im.symbol val f = cs.typeSignature.declaration(newTermName("x")).asTerm val fm: FieldMirror = im.reflectField(f) - println(fm.symbol.isVariable) + println(fm.symbol.isVar) println(fm.get) fm.set(2) println(fm.get) diff --git a/test/files/run/reflection-fieldsymbol-navigation.scala b/test/files/run/reflection-fieldsymbol-navigation.scala index da4612a564..4448724988 100644 --- a/test/files/run/reflection-fieldsymbol-navigation.scala +++ b/test/files/run/reflection-fieldsymbol-navigation.scala @@ -7,9 +7,9 @@ class C { object Test extends App { val x = typeOf[C].member(newTermName("x")).asTerm println(x) - println(x.isVariable) + println(x.isVar) println(x.accessed) - println(x.accessed.asTerm.isVariable) + println(x.accessed.asTerm.isVar) println(x.getter) println(x.setter) } \ No newline at end of file -- cgit v1.2.3 From cb393fcbe35d0a871f23189d791b44be1b826ed2 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Thu, 30 Aug 2012 11:04:58 +0200 Subject: Fix SI-6294. --- src/compiler/scala/tools/nsc/backend/icode/GenICode.scala | 7 +++++-- test/files/pos/t6294.scala | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 test/files/pos/t6294.scala (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 431802d185..739aa2b184 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -121,10 +121,13 @@ abstract class GenICode extends SubComponent { m.native = m.symbol.hasAnnotation(definitions.NativeAttr) if (!m.isAbstractMethod && !m.native) { - if (m.symbol.isAccessor && m.symbol.accessed.hasStaticAnnotation) { + val staticfield = if (m.symbol.isAccessor && m.symbol.accessed.hasStaticAnnotation) { + val compClass = m.symbol.owner.companionClass + compClass.info.findMember(m.symbol.accessed.name, NoFlags, NoFlags, false) + } else NoSymbol + if (staticfield != NoSymbol) { // in companion object accessors to @static fields, we access the static field directly val hostClass = m.symbol.owner.companionClass - val staticfield = hostClass.info.findMember(m.symbol.accessed.name, NoFlags, NoFlags, false) if (m.symbol.isGetter) { ctx1.bb.emit(LOAD_FIELD(staticfield, true) setHostClass hostClass, tree.pos) diff --git a/test/files/pos/t6294.scala b/test/files/pos/t6294.scala new file mode 100644 index 0000000000..c6d39a9cc8 --- /dev/null +++ b/test/files/pos/t6294.scala @@ -0,0 +1,14 @@ + + + +object A { + @annotation.static final val x = 123 +} + + +object B { + println(A.x) +} + + + -- cgit v1.2.3 From 9933140d24603e1ae4e7d3e1256db89b1ea4f46e Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Fri, 31 Aug 2012 16:09:05 -0400 Subject: Fixes SI-6290 by creating real instnaces of language features. --- src/library/scala/language.scala | 14 +++++++------- src/library/scala/languageFeature.scala | 7 +++++++ test/files/run/t6290.scala | 4 ++++ 3 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 test/files/run/t6290.scala (limited to 'test/files') diff --git a/src/library/scala/language.scala b/src/library/scala/language.scala index df2eb0b910..dfe27f8857 100644 --- a/src/library/scala/language.scala +++ b/src/library/scala/language.scala @@ -17,7 +17,7 @@ object language { * of programs. Furthermore, dynamic member selection often relies on reflection, * which is not available on all platforms. */ - implicit lazy val dynamics: dynamics = ??? + implicit lazy val dynamics: dynamics = languageFeature.dynamics /** Only where enabled, postfix operator notation `(expr op)` will be allowed. * @@ -26,7 +26,7 @@ object language { * _Why control it?_ Postfix operators interact poorly with semicolon inference. * Most programmers avoid them for this reason. */ - implicit lazy val postfixOps: postfixOps = ??? + implicit lazy val postfixOps: postfixOps = languageFeature.postfixOps /** Only where enabled, accesses to members of structural types that need * reflection are supported. Reminder: A structural type is a type of the form @@ -42,7 +42,7 @@ object language { * such as ProGuard have problems dealing with it. Even where reflection is available, * reflective dispatch can lead to surprising performance degradations. */ - implicit lazy val reflectiveCalls: reflectiveCalls = ??? + implicit lazy val reflectiveCalls: reflectiveCalls = languageFeature.reflectiveCalls /** Only where enabled, definitions of implicit conversions are allowed. An * implicit conversion is an implicit value of unary function type `A => B`, @@ -65,7 +65,7 @@ object language { * most situations using implicit parameters leads to a better design than * implicit conversions. */ - implicit lazy val implicitConversions: implicitConversions = ??? + implicit lazy val implicitConversions: implicitConversions = languageFeature.implicitConversions /** Only where this flag is enabled, higher-kinded types can be written. * @@ -86,7 +86,7 @@ object language { * enabling also serves as a warning that code involving higher-kinded types * might have to be slightly revised in the future. */ - implicit lazy val higherKinds: higherKinds = ??? + implicit lazy val higherKinds: higherKinds = languageFeature.higherKinds /** Only where enabled, existential types that cannot be expressed as wildcard * types can be written and are allowed in inferred types of values or return @@ -102,7 +102,7 @@ object language { * is generally perceived not to be a good idea. Also, complicated existential types * might be no longer supported in a future simplification of the language. */ - implicit lazy val existentials: existentials = ??? + implicit lazy val existentials: existentials = languageFeature.existentials object experimental { @@ -119,6 +119,6 @@ object language { * _Why control it?_ For their very power, macros can lead to code that is hard * to debug and understand. */ - implicit lazy val macros: macros = ??? + implicit lazy val macros: macros = languageFeature.experimental.macros } } diff --git a/src/library/scala/languageFeature.scala b/src/library/scala/languageFeature.scala index c32f1eb724..39540b4f49 100644 --- a/src/library/scala/languageFeature.scala +++ b/src/library/scala/languageFeature.scala @@ -6,25 +6,32 @@ object languageFeature { @meta.languageFeature("extension of type scala.Dynamic", enableRequired = true) sealed trait dynamics + object dynamics extends dynamics @meta.languageFeature("postfix operator #", enableRequired = false) sealed trait postfixOps + object postfixOps extends postfixOps @meta.languageFeature("reflective access of structural type member #", enableRequired = false) sealed trait reflectiveCalls + object reflectiveCalls extends reflectiveCalls @meta.languageFeature("implicit conversion #", enableRequired = false) sealed trait implicitConversions + object implicitConversions extends implicitConversions @meta.languageFeature("higher-kinded type", enableRequired = false) sealed trait higherKinds + object higherKinds extends higherKinds @meta.languageFeature("#, which cannot be expressed by wildcards, ", enableRequired = false) sealed trait existentials + object existentials extends existentials object experimental { @meta.languageFeature("macro definition", enableRequired = true) sealed trait macros + object macros extends macros } } diff --git a/test/files/run/t6290.scala b/test/files/run/t6290.scala new file mode 100644 index 0000000000..9d05db0d18 --- /dev/null +++ b/test/files/run/t6290.scala @@ -0,0 +1,4 @@ +object Test { + implicit val foo = language.dynamics + def main(args: Array[String]): Unit = () +} -- cgit v1.2.3 From 34893e5d77b683dfd442162f7a5a28b6901c7080 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 31 Aug 2012 17:40:58 -0700 Subject: Fix for SI-6034, covariant value classes. My summary in the ticket was incorrect. The problem was that the class type parameters were being cloned for the method and being allowed to keep their variance. I threw in an assertion for anyone attempting to create a method type with variant type parameters, because hey, why should we allow such madness. --- src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala | 3 ++- src/reflect/scala/reflect/internal/Symbols.scala | 1 + src/reflect/scala/reflect/internal/Types.scala | 9 +++++++-- test/files/pos/t6034.scala | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 test/files/pos/t6034.scala (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 2831afc48e..0820d3e714 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -111,7 +111,8 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { } def extensionMethInfo(extensionMeth: Symbol, origInfo: Type, clazz: Symbol): Type = { - var newTypeParams = cloneSymbolsAtOwner(clazz.typeParams, extensionMeth) + // No variance for method type parameters + var newTypeParams = cloneSymbolsAtOwner(clazz.typeParams, extensionMeth) map (_ resetFlag COVARIANT | CONTRAVARIANT) val thisParamType = appliedType(clazz.typeConstructor, newTypeParams map (_.tpeHK)) val thisParam = extensionMeth.newValueParameter(nme.SELF, extensionMeth.pos) setInfo thisParamType def transform(clonedType: Type): Type = clonedType match { diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 37f41e2868..2e045b08b9 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -516,6 +516,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isTypeParameterOrSkolem = false def isTypeSkolem = false def isTypeMacro = false + def isInvariant = !isCovariant && !isContravariant /** Qualities of Terms, always false for TypeSymbols. */ diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 01f615c5cc..b13b893635 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -3712,10 +3712,15 @@ trait Types extends api.Types { self: SymbolTable => * may or may not be poly? (It filched the standard "canonical creator" name.) */ object GenPolyType { - def apply(tparams: List[Symbol], tpe: Type): Type = ( + def apply(tparams: List[Symbol], tpe: Type): Type = { + tpe match { + case MethodType(_, _) => + assert(tparams forall (_.isInvariant), "Trying to create a method with variant type parameters: " + ((tparams, tpe))) + case _ => + } if (tparams.nonEmpty) typeFun(tparams, tpe) else tpe // it's okay to be forgiving here - ) + } def unapply(tpe: Type): Option[(List[Symbol], Type)] = tpe match { case PolyType(tparams, restpe) => Some((tparams, restpe)) case _ => Some((Nil, tpe)) diff --git a/test/files/pos/t6034.scala b/test/files/pos/t6034.scala new file mode 100644 index 0000000000..3558d7ff0b --- /dev/null +++ b/test/files/pos/t6034.scala @@ -0,0 +1 @@ +final class OptPlus[+A](val x: A) extends AnyVal { } -- cgit v1.2.3 From d9c0cb6165bd60d79bdf764291c417c86623b042 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 21 Aug 2012 07:15:27 +0200 Subject: Suppressed 'possible cause' mis-warning. I have seen this warning a bunch of times and it has not yet been close to right. --- src/compiler/scala/tools/nsc/typechecker/Infer.scala | 11 ++++++++++- test/files/buildmanager/t2650_3/t2650_3.check | 1 - test/files/buildmanager/t2650_4/t2650_4.check | 1 - test/files/neg/not-possible-cause.check | 10 ++++++++++ test/files/neg/not-possible-cause.scala | 3 +++ 5 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 test/files/neg/not-possible-cause.check create mode 100644 test/files/neg/not-possible-cause.scala (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 28636fc76e..1b3602fca2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -280,7 +280,16 @@ trait Infer { def issue(err: AbsTypeError): Unit = context.issue(err) - def isPossiblyMissingArgs(found: Type, req: Type) = (found.resultApprox ne found) && isWeaklyCompatible(found.resultApprox, req) + def isPossiblyMissingArgs(found: Type, req: Type) = ( + false + /** However it is that this condition is expected to imply + * "is possibly missing args", it is too weak. It is + * better to say nothing than to offer misleading guesses. + + (found.resultApprox ne found) + && isWeaklyCompatible(found.resultApprox, req) + */ + ) def explainTypes(tp1: Type, tp2: Type) = withDisambiguation(List(), tp1, tp2)(global.explainTypes(tp1, tp2)) diff --git a/test/files/buildmanager/t2650_3/t2650_3.check b/test/files/buildmanager/t2650_3/t2650_3.check index c109800d9c..5c6326d59f 100644 --- a/test/files/buildmanager/t2650_3/t2650_3.check +++ b/test/files/buildmanager/t2650_3/t2650_3.check @@ -10,6 +10,5 @@ B.scala:2: error: type mismatch; found : a.T (which expands to) Long required: Int - possible cause: missing arguments for method or constructor def x(a: A): Int = a.x ^ diff --git a/test/files/buildmanager/t2650_4/t2650_4.check b/test/files/buildmanager/t2650_4/t2650_4.check index 89536776bd..a4aeaddfbb 100644 --- a/test/files/buildmanager/t2650_4/t2650_4.check +++ b/test/files/buildmanager/t2650_4/t2650_4.check @@ -10,6 +10,5 @@ B.scala:2: error: type mismatch; found : a.T2 (which expands to) Long required: Int - possible cause: missing arguments for method or constructor def x(a: A): Int = a.x ^ diff --git a/test/files/neg/not-possible-cause.check b/test/files/neg/not-possible-cause.check new file mode 100644 index 0000000000..9111cd0d3d --- /dev/null +++ b/test/files/neg/not-possible-cause.check @@ -0,0 +1,10 @@ +not-possible-cause.scala:2: error: type mismatch; + found : a.type (with underlying type A) + required: AnyRef +Note that implicit conversions are not applicable because they are ambiguous: + both method any2stringfmt in object Predef of type (x: Any)scala.runtime.StringFormat + and method any2stringadd in object Predef of type (x: Any)scala.runtime.StringAdd + are possible conversion functions from a.type to AnyRef + def foo[A <: Product](a: A) { type X = a.type } + ^ +one error found diff --git a/test/files/neg/not-possible-cause.scala b/test/files/neg/not-possible-cause.scala new file mode 100644 index 0000000000..83ec24dec8 --- /dev/null +++ b/test/files/neg/not-possible-cause.scala @@ -0,0 +1,3 @@ +object Foo { + def foo[A <: Product](a: A) { type X = a.type } +} -- cgit v1.2.3 From 6cda8a6f972d014f9b73c54a43bb80f99b64adb4 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 21 Aug 2012 12:18:09 +0200 Subject: Fix for SI-6263, futile adaptation. Don't try to implicitly convert an unstable prefix to a stable one by applying a view. As the matrix spoon kid says, "that's impossible." --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 8 ++++++-- test/files/neg/t6263.check | 6 ++++++ test/files/neg/t6263.scala | 6 ++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 test/files/neg/t6263.check create mode 100644 test/files/neg/t6263.scala (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index f6baf02c3e..b6dab13111 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -233,10 +233,11 @@ trait Typers extends Modes with Adaptations with Tags { * @param tree ... * @return ... */ - def checkStable(tree: Tree): Tree = + def checkStable(tree: Tree): Tree = ( if (treeInfo.isExprSafeToInline(tree)) tree else if (tree.isErrorTyped) tree else UnstableTreeError(tree) + ) /** Would tree be a stable (i.e. a pure expression) if the type * of its symbol was not volatile? @@ -5201,7 +5202,10 @@ trait Typers extends Modes with Adaptations with Tags { def typedSingletonTypeTree(tree: SingletonTypeTree) = { val ref1 = checkStable( - typed(tree.ref, EXPRmode | QUALmode | (mode & TYPEPATmode), AnyRefClass.tpe)) + context.withImplicitsDisabled( + typed(tree.ref, EXPRmode | QUALmode | (mode & TYPEPATmode), AnyRefClass.tpe) + ) + ) tree setType ref1.tpe.resultType } diff --git a/test/files/neg/t6263.check b/test/files/neg/t6263.check new file mode 100644 index 0000000000..1c5834e1ca --- /dev/null +++ b/test/files/neg/t6263.check @@ -0,0 +1,6 @@ +t6263.scala:5: error: type mismatch; + found : A.this.c.type (with underlying type C) + required: AnyRef + type t = c.type + ^ +one error found diff --git a/test/files/neg/t6263.scala b/test/files/neg/t6263.scala new file mode 100644 index 0000000000..6575185b5c --- /dev/null +++ b/test/files/neg/t6263.scala @@ -0,0 +1,6 @@ +class C(val a: Any) extends AnyVal +class A { + implicit def c2AnyRef(c: C): AnyRef = new {} + val c = new C(0) + type t = c.type +} -- cgit v1.2.3 From 35316be5a89b2c9622e92594245aaf72a3820c88 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 21 Aug 2012 07:40:28 +0200 Subject: Better errors for Any/AnyRef issues. When an error occurs because some type does not conform to AnyRef (and an AnyRef-derived type would have sufficed) try to say something useful about the situation. This commit also initializes scope members before printing error messages because the + version seems more useful than the - version (taken from one of the checkfile diffs.) - def : - def methodIntIntInt: + def (): X + def methodIntIntInt(x: scala.Int,y: scala.Int): scala.Int --- .../tools/nsc/typechecker/ContextErrors.scala | 108 +++++++++++++-------- .../tools/nsc/typechecker/TypeDiagnostics.scala | 12 ++- .../scala/reflect/internal/Definitions.scala | 28 +++++- src/reflect/scala/reflect/internal/Types.scala | 19 ++-- test/files/neg/any-vs-anyref.check | 64 ++++++++++++ test/files/neg/any-vs-anyref.scala | 29 ++++++ test/files/neg/not-possible-cause.check | 7 +- test/files/neg/t3614.check | 4 +- test/files/neg/t6263.check | 3 + test/files/neg/t900.check | 3 + test/files/run/reflection-equality.check | 106 ++++++++++---------- test/files/run/t5256a.check | 10 +- test/files/run/t5256b.check | 10 +- test/files/run/t5256d.check | 64 ++++++------ test/files/run/t5256e.check | 4 +- test/files/run/t5256f.check | 8 +- 16 files changed, 322 insertions(+), 157 deletions(-) create mode 100644 test/files/neg/any-vs-anyref.check create mode 100644 test/files/neg/any-vs-anyref.scala (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 773d9a6f50..b7195b0349 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -85,10 +85,33 @@ trait ContextErrors { def typeErrorMsg(found: Type, req: Type, possiblyMissingArgs: Boolean) = { def missingArgsMsg = if (possiblyMissingArgs) "\n possible cause: missing arguments for method or constructor" else "" + "type mismatch" + foundReqMsg(found, req) + missingArgsMsg } } + def notAnyRefMessage(found: Type): String = { + val tp = found.widen + def name = tp.typeSymbol.nameString + def parents = tp.parents filterNot isTrivialTopType + def onlyAny = tp.parents forall (_.typeSymbol == AnyClass) + def parents_s = ( if (parents.isEmpty) tp.parents else parents ) mkString ", " + def what = ( + if (tp.typeSymbol.isAbstractType) { + val descr = if (onlyAny) "unbounded" else "bounded only by " + parents_s + s"$name is $descr, which means AnyRef is not a known parent" + } + else if (tp.typeSymbol.isAnonOrRefinementClass) + s"the parents of this type ($parents_s) extend Any, not AnyRef" + else + s"$name extends Any, not AnyRef" + ) + if (isPrimitiveValueType(found)) "" else "\n" + + s"""|Note that $what. + |Such types can participate in value classes, but instances + |cannot appear in singleton types or in reference comparisons.""".stripMargin + } + import ErrorUtils._ trait TyperContextErrors { @@ -296,12 +319,17 @@ trait ContextErrors { else "" ) - companion + semicolon + val notAnyRef = ( + if (ObjectClass.info.member(name).exists) notAnyRefMessage(target) + else "" + ) + companion + notAnyRef + semicolon } + def targetStr = targetKindString + target.directObjectString withAddendum(qual.pos)( - if (name == nme.CONSTRUCTOR) target + " does not have a constructor" - else nameString + " is not a member of " + targetKindString + target.directObjectString + addendum - ) + if (name == nme.CONSTRUCTOR) s"$target does not have a constructor" + else s"$nameString is not a member of $targetStr$addendum" + ) } issueNormalTypeError(sel, errMsg) // the error has to be set for the copied tree, otherwise @@ -1095,44 +1123,42 @@ trait ContextErrors { pre1: String, pre2: String, trailer: String) (isView: Boolean, pt: Type, tree: Tree)(implicit context0: Context) = { if (!info1.tpe.isErroneous && !info2.tpe.isErroneous) { - val coreMsg = - pre1+" "+info1.sym.fullLocationString+" of type "+info1.tpe+"\n "+ - pre2+" "+info2.sym.fullLocationString+" of type "+info2.tpe+"\n "+ - trailer - val errMsg = - if (isView) { - val found = pt.typeArgs(0) - val req = pt.typeArgs(1) - def defaultExplanation = - "Note that implicit conversions are not applicable because they are ambiguous:\n "+ - coreMsg+"are possible conversion functions from "+ found+" to "+req - - def explanation = { - val sym = found.typeSymbol - // Explain some common situations a bit more clearly. - if (AnyRefClass.tpe <:< req) { - if (sym == AnyClass || sym == UnitClass) { - "Note: " + sym.name + " is not implicitly converted to AnyRef. You can safely\n" + - "pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so." - } - else boxedClass get sym match { - case Some(boxed) => - "Note: an implicit exists from " + sym.fullName + " => " + boxed.fullName + ", but\n" + - "methods inherited from Object are rendered ambiguous. This is to avoid\n" + - "a blanket implicit which would convert any " + sym.fullName + " to any AnyRef.\n" + - "You may wish to use a type ascription: `x: " + boxed.fullName + "`." - case _ => - defaultExplanation - } - } - else defaultExplanation - } - - typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req)) + "\n" + explanation - } else { - "ambiguous implicit values:\n "+coreMsg + "match expected type "+pt + def coreMsg = + s"""| $pre1 ${info1.sym.fullLocationString} of type ${info1.tpe} + | $pre2 ${info2.sym.fullLocationString} of type ${info2.tpe} + | $trailer""".stripMargin + def viewMsg = { + val found :: req :: _ = pt.typeArgs + def explanation = { + val sym = found.typeSymbol + // Explain some common situations a bit more clearly. Some other + // failures which have nothing to do with implicit conversions + // per se, but which manifest as implicit conversion conflicts + // involving Any, are further explained from foundReqMsg. + if (AnyRefClass.tpe <:< req) ( + if (sym == AnyClass || sym == UnitClass) ( + s"""|Note: ${sym.name} is not implicitly converted to AnyRef. You can safely + |pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so.""".stripMargin + ) + else boxedClass get sym map (boxed => + s"""|Note: an implicit exists from ${sym.fullName} => ${boxed.fullName}, but + |methods inherited from Object are rendered ambiguous. This is to avoid + |a blanket implicit which would convert any ${sym.fullName} to any AnyRef. + |You may wish to use a type ascription: `x: ${boxed.fullName}`.""".stripMargin + ) getOrElse "" + ) + else + s"""|Note that implicit conversions are not applicable because they are ambiguous: + |${coreMsg}are possible conversion functions from $found to $req""".stripMargin } - context.issueAmbiguousError(AmbiguousTypeError(tree, tree.pos, errMsg)) + typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req)) + ( + if (explanation == "") "" else "\n" + explanation + ) + } + context.issueAmbiguousError(AmbiguousTypeError(tree, tree.pos, + if (isView) viewMsg + else s"ambiguous implicit values:\n${coreMsg}match expected type $pt") + ) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index f0dca64a00..f8a5c401df 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -252,6 +252,13 @@ trait TypeDiagnostics { } "" // no elaborable variance situation found } + + // For found/required errors where AnyRef would have sufficed: + // explain in greater detail. + def explainAnyVsAnyRef(found: Type, req: Type): String = { + if (AnyRefClass.tpe <:< req) notAnyRefMessage(found) else "" + } + // TODO - figure out how to avoid doing any work at all // when the message will never be seen. I though context.reportErrors // being false would do that, but if I return "" under @@ -261,7 +268,10 @@ trait TypeDiagnostics { ";\n found : " + found.toLongString + existentialContext(found) + explainAlias(found) + "\n required: " + req + existentialContext(req) + explainAlias(req) ) - withDisambiguation(Nil, found, req)(baseMessage) + explainVariance(found, req) + ( withDisambiguation(Nil, found, req)(baseMessage) + + explainVariance(found, req) + + explainAnyVsAnyRef(found, req) + ) } case class TypeDiag(tp: Type, sym: Symbol) extends Ordered[TypeDiag] { diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 98d42b724c..c21ebfe997 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -218,6 +218,32 @@ trait Definitions extends api.StandardDefinitions { case _ => null } + /** Fully initialize the symbol, type, or scope. + */ + def fullyInitializeSymbol(sym: Symbol): Symbol = { + sym.initialize + fullyInitializeType(sym.info) + fullyInitializeType(sym.tpe) + sym + } + def fullyInitializeType(tp: Type): Type = { + tp.typeParams foreach fullyInitializeSymbol + tp.paramss.flatten foreach fullyInitializeSymbol + tp + } + def fullyInitializeScope(scope: Scope): Scope = { + scope.sorted foreach fullyInitializeSymbol + scope + } + /** Is this type equivalent to Any, AnyVal, or AnyRef? */ + def isTrivialTopType(tp: Type) = ( + tp =:= AnyClass.tpe + || tp =:= AnyValClass.tpe + || tp =:= AnyRefClass.tpe + ) + /** Does this type have a parent which is none of Any, AnyVal, or AnyRef? */ + def hasNonTrivialParent(tp: Type) = tp.parents exists (t => !isTrivialTopType(tp)) + private def fixupAsAnyTrait(tpe: Type): Type = tpe match { case ClassInfoType(parents, decls, clazz) => if (parents.head.typeSymbol == AnyClass) tpe @@ -383,7 +409,7 @@ trait Definitions extends api.StandardDefinitions { def isRepeated(param: Symbol) = isRepeatedParamType(param.tpe) def isCastSymbol(sym: Symbol) = sym == Any_asInstanceOf || sym == Object_asInstanceOf - def isJavaVarArgsMethod(m: Symbol) = m.isMethod && isJavaVarArgs(m.info.params) + def isJavaVarArgsMethod(m: Symbol) = m.isMethod && isJavaVarArgs(m.info.params) def isJavaVarArgs(params: Seq[Symbol]) = params.nonEmpty && isJavaRepeatedParamType(params.last.tpe) def isScalaVarArgs(params: Seq[Symbol]) = params.nonEmpty && isScalaRepeatedParamType(params.last.tpe) def isVarArgsList(params: Seq[Symbol]) = params.nonEmpty && isRepeatedParamType(params.last.tpe) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index b13b893635..c266e26895 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -1010,7 +1010,11 @@ trait Types extends api.Types { self: SymbolTable => def toLongString = { val str = toString if (str == "type") widen.toString - else if ((str endsWith ".type") && !typeSymbol.isModuleClass) str + " (with underlying type " + widen + ")" + else if ((str endsWith ".type") && !typeSymbol.isModuleClass) + widen match { + case RefinedType(_, _) => "" + widen + case _ => s"$str (with underlying type $widen)" + } else str } @@ -1632,7 +1636,7 @@ trait Types extends api.Types { self: SymbolTable => override def safeToString: String = parentsString(parents) + ( (if (settings.debug.value || parents.isEmpty || (decls.elems ne null)) - decls.mkString("{", "; ", "}") else "") + fullyInitializeScope(decls).mkString("{", "; ", "}") else "") ) } @@ -1819,7 +1823,6 @@ trait Types extends api.Types { self: SymbolTable => false })) } - override def kind = "RefinedType" } @@ -2005,9 +2008,11 @@ trait Types extends api.Types { self: SymbolTable => /** A nicely formatted string with newlines and such. */ def formattedToString: String = - parents.mkString("\n with ") + - (if (settings.debug.value || parents.isEmpty || (decls.elems ne null)) - decls.mkString(" {\n ", "\n ", "\n}") else "") + parents.mkString("\n with ") + ( + if (settings.debug.value || parents.isEmpty || (decls.elems ne null)) + fullyInitializeScope(decls).mkString(" {\n ", "\n ", "\n}") + else "" + ) } object ClassInfoType extends ClassInfoTypeExtractor @@ -2466,7 +2471,7 @@ trait Types extends api.Types { self: SymbolTable => def refinementString = ( if (sym.isStructuralRefinement) ( - decls filter (sym => sym.isPossibleInRefinement && sym.isPublic) + fullyInitializeScope(decls) filter (sym => sym.isPossibleInRefinement && sym.isPublic) map (_.defString) mkString("{", "; ", "}") ) diff --git a/test/files/neg/any-vs-anyref.check b/test/files/neg/any-vs-anyref.check new file mode 100644 index 0000000000..63c4853130 --- /dev/null +++ b/test/files/neg/any-vs-anyref.check @@ -0,0 +1,64 @@ +any-vs-anyref.scala:6: error: type mismatch; + found : a.type (with underlying type A) + required: AnyRef +Note that A is bounded only by Equals, which means AnyRef is not a known parent. +Such types can participate in value classes, but instances +cannot appear in singleton types or in reference comparisons. + def foo1[A <: Product](a: A) = { type X = a.type } + ^ +any-vs-anyref.scala:7: error: type mismatch; + found : a.type (with underlying type A) + required: AnyRef +Note that A is bounded only by Product, Quux, which means AnyRef is not a known parent. +Such types can participate in value classes, but instances +cannot appear in singleton types or in reference comparisons. + def foo2[A <: Product with Quux](a: A) = { type X = a.type } + ^ +any-vs-anyref.scala:8: error: type mismatch; + found : a.type (with underlying type Product) + required: AnyRef +Note that Product extends Any, not AnyRef. +Such types can participate in value classes, but instances +cannot appear in singleton types or in reference comparisons. + def foo3(a: Product) = { type X = a.type } + ^ +any-vs-anyref.scala:9: error: type mismatch; + found : Product with Quux + required: AnyRef +Note that the parents of this type (Product, Quux) extend Any, not AnyRef. +Such types can participate in value classes, but instances +cannot appear in singleton types or in reference comparisons. + def foo4(a: Product with Quux) = { type X = a.type } + ^ +any-vs-anyref.scala:10: error: value eq is not a member of Quux with Product +Note that the parents of this type (Quux, Product) extend Any, not AnyRef. +Such types can participate in value classes, but instances +cannot appear in singleton types or in reference comparisons. + def foo5(x: Quux with Product) = (x eq "abc") && ("abc" eq x) + ^ +any-vs-anyref.scala:11: error: value eq is not a member of Quux with Product{def f: Int} +Note that the parents of this type (Quux, Product) extend Any, not AnyRef. +Such types can participate in value classes, but instances +cannot appear in singleton types or in reference comparisons. + def foo6(x: Quux with Product { def f: Int }) = (x eq "abc") && ("abc" eq x) + ^ +any-vs-anyref.scala:12: error: type mismatch; + found : Quux with Product{def eq(other: String): Boolean} + required: AnyRef +Note that the parents of this type (Quux, Product) extend Any, not AnyRef. +Such types can participate in value classes, but instances +cannot appear in singleton types or in reference comparisons. + def foo7(x: Quux with Product { def eq(other: String): Boolean }) = (x eq "abc") && ("abc" eq x) + ^ +any-vs-anyref.scala:22: error: value eq is not a member of Bippy +Note that Bippy extends Any, not AnyRef. +Such types can participate in value classes, but instances +cannot appear in singleton types or in reference comparisons. + def bad1(x: Bippy, y: Bippy) = x eq y + ^ +any-vs-anyref.scala:27: error: type mismatch; + found : Quux{def g(x: String): String} + required: Quux{def g(x: Int): Int} + f(new Quux { def g(x: String) = x }) + ^ +9 errors found diff --git a/test/files/neg/any-vs-anyref.scala b/test/files/neg/any-vs-anyref.scala new file mode 100644 index 0000000000..8d237fbaec --- /dev/null +++ b/test/files/neg/any-vs-anyref.scala @@ -0,0 +1,29 @@ +trait Quux extends Any +trait QuuxRef extends AnyRef +final class Bippy(val x: Any) extends AnyVal with Quux + +object Foo { + def foo1[A <: Product](a: A) = { type X = a.type } + def foo2[A <: Product with Quux](a: A) = { type X = a.type } + def foo3(a: Product) = { type X = a.type } + def foo4(a: Product with Quux) = { type X = a.type } + def foo5(x: Quux with Product) = (x eq "abc") && ("abc" eq x) + def foo6(x: Quux with Product { def f: Int }) = (x eq "abc") && ("abc" eq x) + def foo7(x: Quux with Product { def eq(other: String): Boolean }) = (x eq "abc") && ("abc" eq x) + + def ok1[A <: QuuxRef](a: A) = { type X = a.type } + def ok2[A <: Product with QuuxRef](a: A) = { type X = a.type } + def ok3(a: QuuxRef) = { type X = a.type } + def ok4(a: Product with QuuxRef) = { type X = a.type } + def ok5(x: QuuxRef with Product) = (x eq "abc") && ("abc" eq x) + def ok6(x: QuuxRef with Product { def f: Int }) = (x eq "abc") && ("abc" eq x) + def ok7(x: QuuxRef { def eq(other: String): Boolean }) = (x eq "abc") && ("abc" eq x) + + def bad1(x: Bippy, y: Bippy) = x eq y +} + +object Bar { + def f(x: Quux { def g(x: Int): Int }): Int = x g 5 + f(new Quux { def g(x: String) = x }) + f(new Quux { def g(x: Int) = x }) +} diff --git a/test/files/neg/not-possible-cause.check b/test/files/neg/not-possible-cause.check index 9111cd0d3d..5c09fa1545 100644 --- a/test/files/neg/not-possible-cause.check +++ b/test/files/neg/not-possible-cause.check @@ -1,10 +1,9 @@ not-possible-cause.scala:2: error: type mismatch; found : a.type (with underlying type A) required: AnyRef -Note that implicit conversions are not applicable because they are ambiguous: - both method any2stringfmt in object Predef of type (x: Any)scala.runtime.StringFormat - and method any2stringadd in object Predef of type (x: Any)scala.runtime.StringAdd - are possible conversion functions from a.type to AnyRef +Note that A is bounded only by Equals, which means AnyRef is not a known parent. +Such types can participate in value classes, but instances +cannot appear in singleton types or in reference comparisons. def foo[A <: Product](a: A) { type X = a.type } ^ one error found diff --git a/test/files/neg/t3614.check b/test/files/neg/t3614.check index 5fdb5cbf1f..0f9c83aa0d 100644 --- a/test/files/neg/t3614.check +++ b/test/files/neg/t3614.check @@ -1,4 +1,4 @@ -t3614.scala:2: error: class type required but AnyRef{def a: } found +t3614.scala:2: error: class type required but AnyRef{def a: Int} found def v = new ({ def a=0 }) ^ -one error found \ No newline at end of file +one error found diff --git a/test/files/neg/t6263.check b/test/files/neg/t6263.check index 1c5834e1ca..9e9c7c615b 100644 --- a/test/files/neg/t6263.check +++ b/test/files/neg/t6263.check @@ -1,6 +1,9 @@ t6263.scala:5: error: type mismatch; found : A.this.c.type (with underlying type C) required: AnyRef +Note that C extends Any, not AnyRef. +Such types can participate in value classes, but instances +cannot appear in singleton types or in reference comparisons. type t = c.type ^ one error found diff --git a/test/files/neg/t900.check b/test/files/neg/t900.check index ff5304a135..6fe26a31ac 100644 --- a/test/files/neg/t900.check +++ b/test/files/neg/t900.check @@ -1,6 +1,9 @@ t900.scala:4: error: type mismatch; found : Foo.this.x.type (with underlying type Foo.this.bar) required: AnyRef +Note that bar is unbounded, which means AnyRef is not a known parent. +Such types can participate in value classes, but instances +cannot appear in singleton types or in reference comparisons. def break(): x.type ^ one error found diff --git a/test/files/run/reflection-equality.check b/test/files/run/reflection-equality.check index be531fbbd3..17c1f6dd70 100644 --- a/test/files/run/reflection-equality.check +++ b/test/files/run/reflection-equality.check @@ -1,53 +1,53 @@ -Type in expressions to have them evaluated. -Type :help for more information. - -scala> - -scala> class X { - def methodIntIntInt(x: Int, y: Int) = x+y -} -defined class X - -scala> - -scala> import scala.reflect.runtime.universe._ -import scala.reflect.runtime.universe._ - -scala> import scala.reflect.runtime.{ currentMirror => cm } -import scala.reflect.runtime.{currentMirror=>cm} - -scala> def im: InstanceMirror = cm.reflect(new X) -im: reflect.runtime.universe.InstanceMirror - -scala> val cs: ClassSymbol = im.symbol -cs: reflect.runtime.universe.ClassSymbol = class X - -scala> val ts: Type = cs.typeSignature -ts: reflect.runtime.universe.Type = -java.lang.Object { - def : - def methodIntIntInt: -} - -scala> val ms: MethodSymbol = ts.declaration(newTermName("methodIntIntInt")).asMethod -ms: reflect.runtime.universe.MethodSymbol = method methodIntIntInt - -scala> val MethodType( _, t1 ) = ms.typeSignature -t1: reflect.runtime.universe.Type = scala.Int - -scala> val t2 = typeOf[scala.Int] -t2: reflect.runtime.universe.Type = Int - -scala> t1 == t2 -res0: Boolean = false - -scala> t1 =:= t2 -res1: Boolean = true - -scala> t1 <:< t2 -res2: Boolean = true - -scala> t2 <:< t1 -res3: Boolean = true - -scala> +Type in expressions to have them evaluated. +Type :help for more information. + +scala> + +scala> class X { + def methodIntIntInt(x: Int, y: Int) = x+y +} +defined class X + +scala> + +scala> import scala.reflect.runtime.universe._ +import scala.reflect.runtime.universe._ + +scala> import scala.reflect.runtime.{ currentMirror => cm } +import scala.reflect.runtime.{currentMirror=>cm} + +scala> def im: InstanceMirror = cm.reflect(new X) +im: reflect.runtime.universe.InstanceMirror + +scala> val cs: ClassSymbol = im.symbol +cs: reflect.runtime.universe.ClassSymbol = class X + +scala> val ts: Type = cs.typeSignature +ts: reflect.runtime.universe.Type = +java.lang.Object { + def (): X + def methodIntIntInt(x: scala.Int,y: scala.Int): scala.Int +} + +scala> val ms: MethodSymbol = ts.declaration(newTermName("methodIntIntInt")).asMethod +ms: reflect.runtime.universe.MethodSymbol = method methodIntIntInt + +scala> val MethodType( _, t1 ) = ms.typeSignature +t1: reflect.runtime.universe.Type = scala.Int + +scala> val t2 = typeOf[scala.Int] +t2: reflect.runtime.universe.Type = Int + +scala> t1 == t2 +res0: Boolean = false + +scala> t1 =:= t2 +res1: Boolean = true + +scala> t1 <:< t2 +res2: Boolean = true + +scala> t2 <:< t1 +res3: Boolean = true + +scala> diff --git a/test/files/run/t5256a.check b/test/files/run/t5256a.check index 518663b3da..7e60139db3 100644 --- a/test/files/run/t5256a.check +++ b/test/files/run/t5256a.check @@ -1,6 +1,6 @@ -class A -A +class A +A Object { - def : - def foo: -} + def (): A + def foo: Nothing +} diff --git a/test/files/run/t5256b.check b/test/files/run/t5256b.check index d6015f2743..a80df6eb30 100644 --- a/test/files/run/t5256b.check +++ b/test/files/run/t5256b.check @@ -1,6 +1,6 @@ -class A -Test.A +class A +Test.A Object { - def : - def foo: -} + def (): Test.A + def foo: Nothing +} diff --git a/test/files/run/t5256d.check b/test/files/run/t5256d.check index dd32c05a93..9742ae572e 100644 --- a/test/files/run/t5256d.check +++ b/test/files/run/t5256d.check @@ -1,32 +1,32 @@ -Type in expressions to have them evaluated. -Type :help for more information. - -scala> - -scala> import scala.reflect.runtime.universe._ -import scala.reflect.runtime.universe._ - -scala> import scala.reflect.runtime.{currentMirror => cm} -import scala.reflect.runtime.{currentMirror=>cm} - -scala> class A { def foo = ??? } -defined class A - -scala> val c = cm.classSymbol(classOf[A]) -c: reflect.runtime.universe.ClassSymbol = class A - -scala> println(c) -class A - -scala> println(c.fullName) -$line8.$read.$iw.$iw.$iw.$iw.A - -scala> println(c.typeSignature) -java.lang.Object { - def : - def foo: -} - -scala> - -scala> +Type in expressions to have them evaluated. +Type :help for more information. + +scala> + +scala> import scala.reflect.runtime.universe._ +import scala.reflect.runtime.universe._ + +scala> import scala.reflect.runtime.{currentMirror => cm} +import scala.reflect.runtime.{currentMirror=>cm} + +scala> class A { def foo = ??? } +defined class A + +scala> val c = cm.classSymbol(classOf[A]) +c: reflect.runtime.universe.ClassSymbol = class A + +scala> println(c) +class A + +scala> println(c.fullName) +$line8.$read.$iw.$iw.$iw.$iw.A + +scala> println(c.typeSignature) +java.lang.Object { + def (): A + def foo: scala.Nothing +} + +scala> + +scala> diff --git a/test/files/run/t5256e.check b/test/files/run/t5256e.check index 6c6de90acc..011115720c 100644 --- a/test/files/run/t5256e.check +++ b/test/files/run/t5256e.check @@ -1,6 +1,6 @@ class A Test.C.A Object { - def : - def foo: + def (): C.this.A + def foo: Nothing } diff --git a/test/files/run/t5256f.check b/test/files/run/t5256f.check index c840793fd5..e0fec85596 100644 --- a/test/files/run/t5256f.check +++ b/test/files/run/t5256f.check @@ -1,12 +1,12 @@ class A1 Test.A1 Object { - def : - def foo: + def (): Test.A1 + def foo: Nothing } class A2 Test.A2 Object { - def : - def foo: + def (): Test.this.A2 + def foo: Nothing } -- cgit v1.2.3 From ce048745e1520c03cd1467933a87f4d5f8b77652 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 1 Sep 2012 09:33:26 -0700 Subject: Fix for SI-6273, repl string interpolation. As usual the hard part is tracing through all the needless abstraction. Begone, 25 layers of parsing error issuing methods! --- .../scala/tools/nsc/ast/parser/Scanners.scala | 8 ++++++-- .../scala/tools/nsc/interpreter/ExprTyper.scala | 2 +- test/files/neg/t5510.check | 2 +- test/files/run/t6273.check | 19 +++++++++++++++++++ test/files/run/t6273.scala | 11 +++++++++++ 5 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 test/files/run/t6273.check create mode 100644 test/files/run/t6273.scala (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index e6bf43fe93..dd0f8fdbe0 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -754,8 +754,12 @@ trait Scanners extends ScannersCommon { } else { val isUnclosedLiteral = !isUnicodeEscape && (ch == SU || (!multiLine && (ch == CR || ch == LF))) if (isUnclosedLiteral) { - syntaxError(if (!multiLine) "unclosed string literal" else "unclosed multi-line string literal") - } else { + if (multiLine) + incompleteInputError("unclosed multi-line string literal") + else + syntaxError("unclosed string literal") + } + else { putChar(ch) nextRawChar() getStringPart(multiLine) diff --git a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala index f49e8d6b59..0f5777d260 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala @@ -37,7 +37,7 @@ trait ExprTyper { } /** Parse a line into a sequence of trees. Returns None if the input is incomplete. */ - def parse(line: String): Option[List[Tree]] = { + def parse(line: String): Option[List[Tree]] = debugging(s"""parse("$line")""") { var isIncomplete = false reporter.withIncompleteHandler((_, _) => isIncomplete = true) { val trees = codeParser.stmts(line) diff --git a/test/files/neg/t5510.check b/test/files/neg/t5510.check index 60da3bed40..04220e79bb 100644 --- a/test/files/neg/t5510.check +++ b/test/files/neg/t5510.check @@ -13,7 +13,7 @@ t5510.scala:5: error: unclosed string literal t5510.scala:6: error: unclosed multi-line string literal val s5 = ""s""" $s1 $s2 s" ^ -t5510.scala:7: error: '}' expected but eof found. +t5510.scala:7: error: unclosed multi-line string literal } ^ 6 errors found diff --git a/test/files/run/t6273.check b/test/files/run/t6273.check new file mode 100644 index 0000000000..c1c18daac2 --- /dev/null +++ b/test/files/run/t6273.check @@ -0,0 +1,19 @@ +Type in expressions to have them evaluated. +Type :help for more information. + +scala> + +scala> val y = 55 +y: Int = 55 + +scala> val x = s""" + y = $y +""" +x: String = +" + y = 55 +" + +scala> + +scala> diff --git a/test/files/run/t6273.scala b/test/files/run/t6273.scala new file mode 100644 index 0000000000..ed0fd452e0 --- /dev/null +++ b/test/files/run/t6273.scala @@ -0,0 +1,11 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def tq = "\"\"\"" + def code = s""" +val y = 55 +val x = s$tq + y = $$y +$tq + """ +} -- cgit v1.2.3 From 24a54107f3e3e72a079ebe7e425671e3307d1237 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 3 Sep 2012 15:19:47 -0700 Subject: Made Dynamic extend Any. So it can be used in value classes. --- src/library/scala/Dynamic.scala | 10 +++++----- test/files/run/dynamic-anyval.check | 4 ++++ test/files/run/dynamic-anyval.scala | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 test/files/run/dynamic-anyval.check create mode 100644 test/files/run/dynamic-anyval.scala (limited to 'test/files') diff --git a/src/library/scala/Dynamic.scala b/src/library/scala/Dynamic.scala index faf834d310..3bcb2f1c90 100644 --- a/src/library/scala/Dynamic.scala +++ b/src/library/scala/Dynamic.scala @@ -9,11 +9,11 @@ package scala /** A marker trait that enables dynamic invocations. Instances `x` of this - * trait allow method invocations `x.meth(args)` for arbitrary method - * names `meth` and argument lists `args` as well as field accesses + * trait allow method invocations `x.meth(args)` for arbitrary method + * names `meth` and argument lists `args` as well as field accesses * `x.field` for arbitrary field names `field`. * - * If a call is not natively supported by `x` (i.e. if type checking + * If a call is not natively supported by `x` (i.e. if type checking * fails), it is rewritten according to the following rules: * * {{{ @@ -23,12 +23,12 @@ package scala * foo.field ~~> foo.selectDynamic("field") * foo.varia = 10 ~~> foo.updateDynamic("varia")(10) * foo.arr(10) = 13 ~~> foo.selectDynamic("arr").update(10, 13) - * foo.arr(10) ~~> foo.applyDynamics("arr")(10) + * foo.arr(10) ~~> foo.applyDynamic("arr")(10) * }}} * * As of Scala 2.10, defining direct or indirect subclasses of this trait * is only possible if the language feature `dynamics` is enabled. */ -trait Dynamic +trait Dynamic extends Any diff --git a/test/files/run/dynamic-anyval.check b/test/files/run/dynamic-anyval.check new file mode 100644 index 0000000000..dee7bef8e8 --- /dev/null +++ b/test/files/run/dynamic-anyval.check @@ -0,0 +1,4 @@ +().dingo(bippy, 5) +List(1, 2, 3).dingo(bippy, 5) +().dingo(bippy, 5) +List(1, 2, 3).dingo(bippy, 5) diff --git a/test/files/run/dynamic-anyval.scala b/test/files/run/dynamic-anyval.scala new file mode 100644 index 0000000000..605503d377 --- /dev/null +++ b/test/files/run/dynamic-anyval.scala @@ -0,0 +1,22 @@ +import scala.language.dynamics + +object Test { + implicit class DynamicValue[T](val value: T) extends AnyVal with Dynamic { + def applyDynamic(name: String)(args: Any*) = println(s"""$this.$name(${args mkString ", "})""") + override def toString = "" + value + } + implicit class DynamicValue2[T](val value: T) extends Dynamic { + def applyDynamic(name: String)(args: Any*) = println(s"""$this.$name(${args mkString ", "})""") + override def toString = "" + value + } + + def f[T](x: DynamicValue[T]) = x.dingo("bippy", 5) + def g[T](x: DynamicValue2[T]) = x.dingo("bippy", 5) + + def main(args: Array[String]): Unit = { + f(()) + f(List(1, 2, 3)) + g(()) + g(List(1, 2, 3)) + } +} -- cgit v1.2.3 From 4f932df552fd2a9e1af31bc3b5fbbfeeaa15feed Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Tue, 4 Sep 2012 10:50:21 +0200 Subject: Allow nested calls to `askForResponse` in the presentation compiler. Fix #6312. review by @odersky,@lrytz. --- .../tools/nsc/interactive/CompilerControl.scala | 16 +++++++++++----- test/files/presentation/recursive-ask.check | 4 ++++ .../presentation/recursive-ask/RecursiveAsk.scala | 20 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 test/files/presentation/recursive-ask.check create mode 100644 test/files/presentation/recursive-ask/RecursiveAsk.scala (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala index 6acd6d2382..3de2359ce3 100644 --- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala +++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala @@ -257,12 +257,18 @@ trait CompilerControl { self: Global => */ def askForResponse[A](op: () => A): Response[A] = { val r = new Response[A] - val ir = scheduler askDoQuickly op - ir onComplete { - case Left(result) => r set result - case Right(exc) => r raise exc + if (self.onCompilerThread) { + try { r set op() } + catch { case exc: Throwable => r raise exc } + r + } else { + val ir = scheduler askDoQuickly op + ir onComplete { + case Left(result) => r set result + case Right(exc) => r raise exc + } + r } - r } def onCompilerThread = Thread.currentThread == compileRunner diff --git a/test/files/presentation/recursive-ask.check b/test/files/presentation/recursive-ask.check new file mode 100644 index 0000000000..357d2cf879 --- /dev/null +++ b/test/files/presentation/recursive-ask.check @@ -0,0 +1,4 @@ +[ outer] askForResponse +[nested] askForResponse +passed +done diff --git a/test/files/presentation/recursive-ask/RecursiveAsk.scala b/test/files/presentation/recursive-ask/RecursiveAsk.scala new file mode 100644 index 0000000000..b0e29b3fd3 --- /dev/null +++ b/test/files/presentation/recursive-ask/RecursiveAsk.scala @@ -0,0 +1,20 @@ +import scala.tools.nsc.interactive.tests._ + +object Test extends InteractiveTest { + override def execute(): Unit = recursiveAskForResponse() + + def recursiveAskForResponse() { + val res0 = compiler.askForResponse( () => { + println("[ outer] askForResponse") + val res = compiler.askForResponse( () => { println("[nested] askForResponse") }) + println (res.get(5000) match { + case Some(_) => "passed" + case None => "timeout" + }) + }) + + res0.get + + println("done") + } +} -- cgit v1.2.3 From ec39e1ca82ea26429c140f120831b01cae65853c Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 4 Sep 2012 14:33:38 +0200 Subject: SI-6274 Fix owners when eta-expanding function with byName param When eta-expanding a function that takes a by-name param the local definition for the corresponding argument is a function-0 val eta$1 = () => { argument-to-by-name } If there are any definitinos in the `argument-to-by-name`, the symbol owner needs to be changed to the anonymous function's symbol. To know the function symbol in eta expand, we need to type-check the function, and therefore pass the `Typer` instance to `etaExpand`. --- src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala | 8 ++++++-- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 2 +- test/files/pos/t6274.scala | 13 +++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 test/files/pos/t6274.scala (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index 9e175fa516..b04a736fd3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -47,7 +47,7 @@ trait EtaExpansion { self: Analyzer => * tree is already attributed *

*/ - def etaExpand(unit : CompilationUnit, tree: Tree): Tree = { + def etaExpand(unit : CompilationUnit, tree: Tree, typer: Typer): Tree = { val tpe = tree.tpe var cnt = 0 // for NoPosition def freshName() = { @@ -69,7 +69,11 @@ trait EtaExpansion { self: Analyzer => val vname: Name = freshName() // Problem with ticket #2351 here defs += atPos(tree.pos) { - val rhs = if (byName) Function(List(), tree) else tree + val rhs = if (byName) { + val res = typer.typed(Function(List(), tree)) + new ChangeOwnerTraverser(typer.context.owner, res.symbol) traverse tree // SI-6274 + res + } else tree ValDef(Modifiers(SYNTHETIC), vname.toTermName, TypeTree(), rhs) } atPos(tree.pos.focus) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 1845776c2d..34cb0fa59c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -886,7 +886,7 @@ trait Typers extends Modes with Adaptations with Tags { if (!meth.isConstructor && !meth.isTermMacro && isFunctionType(pt)) { // (4.2) debuglog("eta-expanding " + tree + ":" + tree.tpe + " to " + pt) checkParamsConvertible(tree, tree.tpe) - val tree0 = etaExpand(context.unit, tree) + val tree0 = etaExpand(context.unit, tree, this) // println("eta "+tree+" ---> "+tree0+":"+tree0.tpe+" undet: "+context.undetparams+ " mode: "+Integer.toHexString(mode)) if (context.undetparams.nonEmpty) { diff --git a/test/files/pos/t6274.scala b/test/files/pos/t6274.scala new file mode 100644 index 0000000000..cf769fc72d --- /dev/null +++ b/test/files/pos/t6274.scala @@ -0,0 +1,13 @@ +trait Crash { + + def foo(i: => Int) (j: Int): Int + + def t = { + // var count = 0 + foo { + var count = 0 + count + } _ + } + +} -- cgit v1.2.3 From 3415436b67ae7889a11ce2537576ca49b328aecc Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Tue, 4 Sep 2012 20:36:47 +0200 Subject: Fixed positions in de-aliased special symbols and for automatically added `apply` methods. Fixed #5064, thanks to @paulp who showed the right direction (and how to test it). --- .../scala/tools/nsc/typechecker/Typers.scala | 4 ++-- test/files/run/t5064.check | 25 ++++++++++++++++++++++ test/files/run/t5064.scala | 23 ++++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 test/files/run/t5064.check create mode 100644 test/files/run/t5064.scala (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 1845776c2d..84eef4d190 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -579,7 +579,7 @@ trait Typers extends Modes with Adaptations with Tags { // to notice exhaustiveness and to generate good code when // List extractors are mixed with :: patterns. See Test5 in lists.scala. def dealias(sym: Symbol) = - (atPos(tree.pos) {gen.mkAttributedRef(sym)}, sym.owner.thisType) + (atPos(tree.pos.makeTransparent) {gen.mkAttributedRef(sym)} setPos tree.pos, sym.owner.thisType) sym.name match { case nme.List => return dealias(ListModule) case nme.Seq => return dealias(SeqModule) @@ -1057,7 +1057,7 @@ trait Typers extends Modes with Adaptations with Tags { case other => other } - typed(atPos(tree.pos)(Select(qual, nme.apply)), mode, pt) + typed(atPos(tree.pos)(Select(qual setPos tree.pos.makeTransparent, nme.apply)), mode, pt) } // begin adapt diff --git a/test/files/run/t5064.check b/test/files/run/t5064.check new file mode 100644 index 0000000000..077006abd9 --- /dev/null +++ b/test/files/run/t5064.check @@ -0,0 +1,25 @@ +[12] T5064.super.() +[12] T5064.super. +[12] this +[16:23] immutable.this.List.apply(scala.this.Predef.wrapIntArray(Array[Int]{1})) +[16:20] immutable.this.List.apply +<16:20> immutable.this.List +<16:20> immutable.this +[16:23] scala.this.Predef.wrapIntArray(Array[Int]{1}) +[20] scala.this.Predef.wrapIntArray +[20] scala.this.Predef +[20] scala.this +[26:32] collection.this.Seq.apply(scala.this.Predef.wrapIntArray(Array[Int]{1})) +[26:29] collection.this.Seq.apply +<26:29> collection.this.Seq +<26:29> collection.this +[26:32] scala.this.Predef.wrapIntArray(Array[Int]{1}) +[29] scala.this.Predef.wrapIntArray +[29] scala.this.Predef +[29] scala.this +[35:39] immutable.this.List +<35:39> immutable.this +[42:45] collection.this.Seq +<42:45> collection.this +[48:51] immutable.this.Nil +<48:51> immutable.this diff --git a/test/files/run/t5064.scala b/test/files/run/t5064.scala new file mode 100644 index 0000000000..35f0951765 --- /dev/null +++ b/test/files/run/t5064.scala @@ -0,0 +1,23 @@ +import scala.tools.partest._ + +object Test extends CompilerTest { + import global._ + override def extraSettings = super.extraSettings + " -Yrangepos" + override def sources = List( + """|class T5064 { + | List(1) + | Seq(1) + | List + | Seq + | Nil + |}""".stripMargin + ) + def check(source: String, unit: CompilationUnit) { + for (ClassDef(_, _, _, Template(_, _, stats)) <- unit.body ; stat <- stats ; t <- stat) { + t match { + case _: Select | _: Apply | _: This => println("%-15s %s".format(t.pos.show, t)) + case _ => + } + } + } +} \ No newline at end of file -- cgit v1.2.3 From 5b9b394d99bd7e4446e0f15475b34ec287d91685 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 4 Sep 2012 14:51:05 -0700 Subject: Removing duplication from Duration. I don't know what good it is to have code review if we are checking in code like this. We must raise the bar, people. When the justification for code being in the standard library is borderline at best - as it is here - then the code must be of exceptional quality. This code is not of exceptional quality. Mostly these are not behavioral changes, but: - I removed finite_? as it is a gratuitous deviation from every isXXX method in the world. This isn't ruby. - I removed all the regexps, which only made things complicated - I removed all the unnecessary casts, which is to say, all of them - I made more things final, sealed, and private - The unapply structure was all wrong; returning Option[Duration] on the string unapply meant you'd have to say case Duration(Duration(x, y)) => ... So I fixed apply and unapply to be symmetric. - And I removed the "parse" method, since it was doing what apply is supposed to do. There's a test case to exercise accessing it from java, which also reveals what I hope are bugs. Thanks to viktor klang for DurationConversions. --- src/library/scala/concurrent/util/Duration.scala | 506 ++++++++--------------- test/files/jvm/duration-java.check | 362 ++++++++++++++++ test/files/jvm/duration-java/Test.java | 38 ++ 3 files changed, 578 insertions(+), 328 deletions(-) create mode 100644 test/files/jvm/duration-java.check create mode 100644 test/files/jvm/duration-java/Test.java (limited to 'test/files') diff --git a/src/library/scala/concurrent/util/Duration.scala b/src/library/scala/concurrent/util/Duration.scala index bab664727e..bf0f3006f1 100644 --- a/src/library/scala/concurrent/util/Duration.scala +++ b/src/library/scala/concurrent/util/Duration.scala @@ -26,76 +26,68 @@ object Deadline { def now: Deadline = Deadline(Duration(System.nanoTime, NANOSECONDS)) } +// TODO: "Inf", "PlusInf", "MinusInf", where did these names come from? +// TODO: Duration.create(n, DAYS) == Duration(Long.MaxValue, NANOSECONDS) forall (n: Double) >= 106752d object Duration { implicit def timeLeft(implicit d: Deadline): Duration = d.timeLeft - def apply(length: Long, unit: TimeUnit): FiniteDuration = new FiniteDuration(length, unit) def apply(length: Double, unit: TimeUnit): FiniteDuration = fromNanos(unit.toNanos(1) * length) - def apply(length: Long, unit: String): FiniteDuration = new FiniteDuration(length, Duration.timeUnit(unit)) + def apply(length: Long, unit: TimeUnit): FiniteDuration = new FiniteDuration(length, unit) + def apply(length: Long, unit: String): FiniteDuration = new FiniteDuration(length, Duration.timeUnit(unit)) /** - * Construct a Duration by parsing a String. In case of a format error, a - * RuntimeException is thrown. See `unapply(String)` for more information. - */ - def apply(s: String): Duration = unapply(s) getOrElse sys.error("format error " + s) - - private val RE = ("""^\s*([\+|-]?\d+(?:\.\d+)?)\s*""" + // length part - "(?:" + // units are distinguished in separate match groups - "(d|day|days)|" + - "(h|hour|hours)|" + - "(min|minute|minutes)|" + - "(s|sec|second|seconds)|" + - "(ms|milli|millis|millisecond|milliseconds)|" + - "(µs|micro|micros|microsecond|microseconds)|" + - "(ns|nano|nanos|nanosecond|nanoseconds)" + - """)\s*$""").r // close the non-capturing group - private val REinf = """^\s*(?:\+|Plus)?Inf\s*$""".r - private val REminf = """^\s*(?:-|Minus)Inf\s*""".r - - /** - * Deconstruct a Duration into `Long` length and [[java.util.concurrent.TimeUnit]] if it is a - * [[scala.util.concurrent.FiniteDuration]]. - * - * @param d Duration to be deconstructed. + * Parse String into Duration. Format is `""`, where + * whitespace is allowed before, between and after the parts. Infinities are + * designated by `"Inf"`, `"PlusInf"`, `"+Inf"` and `"-Inf"` or `"MinusInf"`. + * Throws exception if format is not parseable. */ - def unapply(d: Duration): Option[(Long, TimeUnit)] = { - if (d.finite_?) { - Some((d.length, d.unit)) - } else { - None + def apply(s: String): Duration = { + val s1: String = s filterNot (_.isWhitespace) + s1 match { + case "Inf" | "PlusInf" | "+Inf" => Inf + case "MinusInf" | "-Inf" => MinusInf + case _ => + val unitName = s1.reverse takeWhile (_.isLetter) reverse; + def length = JDouble.parseDouble(s1 dropRight unitName.length) + timeUnit get unitName match { + case Some(unit) => Duration(length, unit) + case _ => sys.error("format error " + s) + } } } - /** - * Parse String, return None if no match. Format is `""`, where - * whitespace is allowed before, between and after the parts. Infinities are - * designated by `"Inf"`, `"PlusInf"`, `"+Inf"` and `"-Inf"` or `"MinusInf"`. - */ - def unapply(s: String): Option[Duration] = s match { - case RE(length, d, h, m, s, ms, mus, ns) ⇒ - if (d ne null) - Some(Duration(JDouble.parseDouble(length), DAYS)) - else if (h ne null) - Some(Duration(JDouble.parseDouble(length), HOURS)) - else if (m ne null) - Some(Duration(JDouble.parseDouble(length), MINUTES)) - else if (s ne null) - Some(Duration(JDouble.parseDouble(length), SECONDS)) - else if (ms ne null) - Some(Duration(JDouble.parseDouble(length), MILLISECONDS)) - else if (mus ne null) - Some(Duration(JDouble.parseDouble(length), MICROSECONDS)) - else if (ns ne null) - Some(Duration(JDouble.parseDouble(length), NANOSECONDS)) - else - sys.error("made some error in regex (should not be possible)") - case REinf() ⇒ Some(Inf) - case REminf() ⇒ Some(MinusInf) - case _ ⇒ None + // "ms milli millisecond" -> List("ms", "milli", "millis", "millisecond", "milliseconds") + private def words(s: String) = (s.trim split "\\s+").toList + private def expandLabels(labels: String): List[String] = { + val hd :: rest = words(labels) + hd :: rest.flatMap(s => List(s, s + "s")) } + private val timeUnitLabels = List( + DAYS -> "d day", + HOURS -> "h hour", + MINUTES -> "min minute", + SECONDS -> "s sec second", + MILLISECONDS -> "ms milli millisecond", + MICROSECONDS -> "µs micro microsecond", + NANOSECONDS -> "ns nano nanosecond" + ) + + // TimeUnit => standard label + protected[util] val timeUnitName: Map[TimeUnit, String] = + timeUnitLabels.toMap mapValues (s => words(s).last) toMap + + // Label => TimeUnit + protected[util] val timeUnit: Map[String, TimeUnit] = + timeUnitLabels flatMap { case (unit, names) => expandLabels(names) map (_ -> unit) } toMap + + def unapply(s: String): Option[(Long, TimeUnit)] = + ( try Some(apply(s)) catch { case _: RuntimeException => None } ) flatMap unapply + + def unapply(d: Duration): Option[(Long, TimeUnit)] = + if (d.isFinite) Some((d.length, d.unit)) else None def fromNanos(nanos: Double): FiniteDuration = - fromNanos((nanos + 0.5).asInstanceOf[Long]) + fromNanos((nanos + 0.5).toLong) def fromNanos(nanos: Long): FiniteDuration = { if (nanos % 86400000000000L == 0) { @@ -118,72 +110,62 @@ object Duration { /** * Parse TimeUnit from string representation. */ - protected[util] def timeUnit(unit: String): TimeUnit = unit.toLowerCase match { - case "d" | "day" | "days" => DAYS - case "h" | "hour" | "hours" => HOURS - case "min" | "minute" | "minutes" => MINUTES - case "s" | "sec" | "second" | "seconds" => SECONDS - case "ms" | "milli" | "millis" | "millisecond" | "milliseconds" => MILLISECONDS - case "µs" | "micro" | "micros" | "microsecond" | "microseconds" => MICROSECONDS - case "ns" | "nano" | "nanos" | "nanosecond" | "nanoseconds" => NANOSECONDS - } val Zero: FiniteDuration = new FiniteDuration(0, NANOSECONDS) - val Undefined: Duration = new Duration with Infinite { + + object Undefined extends Infinite { + private def fail(what: String) = throw new IllegalArgumentException(s"cannot $what Undefined duration") + override def toString = "Duration.Undefined" - override def equals(other: Any) = other.asInstanceOf[AnyRef] eq this - override def +(other: Duration): Duration = throw new IllegalArgumentException("cannot add Undefined duration") - override def -(other: Duration): Duration = throw new IllegalArgumentException("cannot subtract Undefined duration") - override def *(factor: Double): Duration = throw new IllegalArgumentException("cannot multiply Undefined duration") - override def /(factor: Double): Duration = throw new IllegalArgumentException("cannot divide Undefined duration") - override def /(other: Duration): Double = throw new IllegalArgumentException("cannot divide Undefined duration") - def compare(other: Duration) = throw new IllegalArgumentException("cannot compare Undefined duration") - def unary_- : Duration = throw new IllegalArgumentException("cannot negate Undefined duration") + override def equals(other: Any) = this eq other.asInstanceOf[AnyRef] + override def +(other: Duration): Duration = fail("add") + override def -(other: Duration): Duration = fail("subtract") + override def *(factor: Double): Duration = fail("multiply") + override def /(factor: Double): Duration = fail("divide") + override def /(other: Duration): Double = fail("divide") + def compare(other: Duration) = fail("compare") + def unary_- : Duration = fail("negate") } - trait Infinite { - this: Duration => - - def +(other: Duration): Duration = - other match { - case _: this.type => this - case _: Infinite => throw new IllegalArgumentException("illegal addition of infinities") - case _ => this - } + sealed abstract class Infinite extends Duration { + def +(other: Duration): Duration = other match { + case x: Infinite if x ne this => throw new IllegalArgumentException("illegal addition of infinities") + case _ => this + } + // Is this really intended to throw if the argument is "this" but otherwise return this? def -(other: Duration): Duration = - other match { - case _: this.type => throw new IllegalArgumentException("illegal subtraction of infinities") - case _ => this - } + if (other ne this) this + else throw new IllegalArgumentException("illegal subtraction of infinities") + def *(factor: Double): Duration = this def /(factor: Double): Duration = this - def /(other: Duration): Double = - other match { - case _: Infinite => throw new IllegalArgumentException("illegal division of infinities") - // maybe questionable but pragmatic: Inf / 0 => Inf - case x => Double.PositiveInfinity * (if ((this > Zero) ^ (other >= Zero)) -1 else 1) - } - - def finite_? = false - - def length: Long = throw new IllegalArgumentException("length not allowed on infinite Durations") - def unit: TimeUnit = throw new IllegalArgumentException("unit not allowed on infinite Durations") - def toNanos: Long = throw new IllegalArgumentException("toNanos not allowed on infinite Durations") - def toMicros: Long = throw new IllegalArgumentException("toMicros not allowed on infinite Durations") - def toMillis: Long = throw new IllegalArgumentException("toMillis not allowed on infinite Durations") - def toSeconds: Long = throw new IllegalArgumentException("toSeconds not allowed on infinite Durations") - def toMinutes: Long = throw new IllegalArgumentException("toMinutes not allowed on infinite Durations") - def toHours: Long = throw new IllegalArgumentException("toHours not allowed on infinite Durations") - def toDays: Long = throw new IllegalArgumentException("toDays not allowed on infinite Durations") - def toUnit(unit: TimeUnit): Double = throw new IllegalArgumentException("toUnit not allowed on infinite Durations") + def /(other: Duration): Double = other match { + case _: Infinite => throw new IllegalArgumentException("illegal division of infinities") + // maybe questionable but pragmatic: Inf / 0 => Inf + case x => Double.PositiveInfinity * (if ((this > Zero) ^ (other >= Zero)) -1 else 1) + } + + final def isFinite() = false + private def fail(what: String) = throw new IllegalArgumentException(s"$what not allowed on infinite Durations") + def length: Long = fail("length") + def toNanos: Long = fail("toNanos") + def toMicros: Long = fail("toMicros") + def toMillis: Long = fail("toMillis") + def toSeconds: Long = fail("toSeconds") + def toMinutes: Long = fail("toMinutes") + def toHours: Long = fail("toHours") + def toDays: Long = fail("toDays") + + def unit: TimeUnit = fail("unit") + def toUnit(unit: TimeUnit): Double = fail("toUnit") } /** * Infinite duration: greater than any other and not equal to any other, * including itself. */ - val Inf: Duration = new Duration with Infinite { + val Inf: Infinite = new Infinite { override def toString = "Duration.Inf" def compare(other: Duration) = if (other eq this) 0 else 1 def unary_- : Duration = MinusInf @@ -193,17 +175,17 @@ object Duration { * Infinite negative duration: lesser than any other and not equal to any other, * including itself. */ - val MinusInf: Duration = new Duration with Infinite { + val MinusInf: Infinite = new Infinite { override def toString = "Duration.MinusInf" def compare(other: Duration) = if (other eq this) 0 else -1 def unary_- : Duration = Inf } // Java Factories - def create(length: Long, unit: TimeUnit): FiniteDuration = apply(length, unit) + def create(length: Long, unit: TimeUnit): FiniteDuration = apply(length, unit) def create(length: Double, unit: TimeUnit): FiniteDuration = apply(length, unit) - def create(length: Long, unit: String): FiniteDuration = apply(length, unit) - def parse(s: String): Duration = unapply(s).get + def create(length: Long, unit: String): FiniteDuration = apply(length, unit) + def create(s: String): Duration = apply(s) implicit object DurationIsOrdered extends Ordering[Duration] { def compare(a: Duration, b: Duration) = a compare b @@ -261,23 +243,22 @@ abstract class Duration extends Serializable with Ordered[Duration] { def /(factor: Double): Duration def /(other: Duration): Double def unary_- : Duration - def finite_? : Boolean + def isFinite(): Boolean def min(other: Duration): Duration = if (this < other) this else other def max(other: Duration): Duration = if (this > other) this else other def fromNow: Deadline = Deadline.now + this // Java API - def lt(other: Duration) = this < other - def lteq(other: Duration) = this <= other - def gt(other: Duration) = this > other - def gteq(other: Duration) = this >= other - def plus(other: Duration) = this + other + def div(factor: Double) = this / factor + def div(other: Duration) = this / other + def gt(other: Duration) = this > other + def gteq(other: Duration) = this >= other + def lt(other: Duration) = this < other + def lteq(other: Duration) = this <= other def minus(other: Duration) = this - other - def mul(factor: Double) = this * factor - def div(factor: Double) = this / factor - def div(other: Duration) = this / other - def neg() = -this - def isFinite() = finite_? + def mul(factor: Double) = this * factor + def neg() = -this + def plus(other: Duration) = this + other } object FiniteDuration { @@ -286,252 +267,121 @@ object FiniteDuration { } def apply(length: Long, unit: TimeUnit) = new FiniteDuration(length, unit) - - def apply(length: Long, unit: String) = new FiniteDuration(length, Duration.timeUnit(unit)) - + def apply(length: Long, unit: String) = new FiniteDuration(length, Duration.timeUnit(unit)) } class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration { import Duration._ - def toNanos = unit.toNanos(length) - def toMicros = unit.toMicros(length) - def toMillis = unit.toMillis(length) + def toNanos = unit.toNanos(length) + def toMicros = unit.toMicros(length) + def toMillis = unit.toMillis(length) def toSeconds = unit.toSeconds(length) def toMinutes = unit.toMinutes(length) - def toHours = unit.toHours(length) - def toDays = unit.toDays(length) + def toHours = unit.toHours(length) + def toDays = unit.toDays(length) def toUnit(u: TimeUnit) = toNanos.toDouble / NANOSECONDS.convert(1, u) - override def toString = this match { - case Duration(1, DAYS) => "1 day" - case Duration(x, DAYS) => x + " days" - case Duration(1, HOURS) => "1 hour" - case Duration(x, HOURS) => x + " hours" - case Duration(1, MINUTES) => "1 minute" - case Duration(x, MINUTES) => x + " minutes" - case Duration(1, SECONDS) => "1 second" - case Duration(x, SECONDS) => x + " seconds" - case Duration(1, MILLISECONDS) => "1 millisecond" - case Duration(x, MILLISECONDS) => x + " milliseconds" - case Duration(1, MICROSECONDS) => "1 microsecond" - case Duration(x, MICROSECONDS) => x + " microseconds" - case Duration(1, NANOSECONDS) => "1 nanosecond" - case Duration(x, NANOSECONDS) => x + " nanoseconds" - } - - def compare(other: Duration) = - if (other.finite_?) { - val me = toNanos - val o = other.toNanos - if (me > o) 1 else if (me < o) -1 else 0 - } else -other.compare(this) + private def unitString = timeUnitName(unit) + ( if (length == 1) "" else "s" ) + override def toString = "" + length + " " + unitString - def +(other: Duration) = { - if (!other.finite_?) { - other - } else { - val nanos = toNanos + other.asInstanceOf[FiniteDuration].toNanos - fromNanos(nanos) - } + def compare(other: Duration) = other match { + case x: FiniteDuration => toNanos compare x.toNanos + case _ => -(other compare this) } - - def -(other: Duration) = { - if (!other.finite_?) { - other - } else { - val nanos = toNanos - other.asInstanceOf[FiniteDuration].toNanos - fromNanos(nanos) - } + def +(other: Duration) = other match { + case x: FiniteDuration => fromNanos(toNanos + x.toNanos) + case _ => other + } + def -(other: Duration) = other match { + case x: FiniteDuration => fromNanos(toNanos - x.toNanos) + case _ => other } def *(factor: Double) = fromNanos(toNanos.toDouble * factor) def /(factor: Double) = fromNanos(toNanos.toDouble / factor) - def /(other: Duration) = if (other.finite_?) toNanos.toDouble / other.toNanos else 0 + def /(other: Duration) = if (other.isFinite) toNanos.toDouble / other.toNanos else 0 def unary_- = Duration(-length, unit) - def finite_? = true - - override def equals(other: Any) = - other.isInstanceOf[FiniteDuration] && - toNanos == other.asInstanceOf[FiniteDuration].toNanos + final def isFinite() = true - override def hashCode = toNanos.asInstanceOf[Int] + override def equals(other: Any) = other match { + case x: FiniteDuration => toNanos == x.toNanos + case _ => super.equals(other) + } + override def hashCode = toNanos.toInt } -class DurationInt(n: Int) { +trait DurationConversions extends Any { import duration.Classifier + protected def durationIn(unit: TimeUnit): FiniteDuration - def nanoseconds = Duration(n, NANOSECONDS) - def nanos = Duration(n, NANOSECONDS) - def nanosecond = Duration(n, NANOSECONDS) - def nano = Duration(n, NANOSECONDS) + def nanoseconds = durationIn(NANOSECONDS) + def nanos = nanoseconds + def nanosecond = nanoseconds + def nano = nanoseconds - def microseconds = Duration(n, MICROSECONDS) - def micros = Duration(n, MICROSECONDS) - def microsecond = Duration(n, MICROSECONDS) - def micro = Duration(n, MICROSECONDS) + def microseconds = durationIn(MICROSECONDS) + def micros = microseconds + def microsecond = microseconds + def micro = microseconds - def milliseconds = Duration(n, MILLISECONDS) - def millis = Duration(n, MILLISECONDS) - def millisecond = Duration(n, MILLISECONDS) - def milli = Duration(n, MILLISECONDS) + def milliseconds = durationIn(MILLISECONDS) + def millis = milliseconds + def millisecond = milliseconds + def milli = milliseconds - def seconds = Duration(n, SECONDS) - def second = Duration(n, SECONDS) + def seconds = durationIn(SECONDS) + def second = seconds - def minutes = Duration(n, MINUTES) - def minute = Duration(n, MINUTES) + def minutes = durationIn(MINUTES) + def minute = minutes - def hours = Duration(n, HOURS) - def hour = Duration(n, HOURS) + def hours = durationIn(HOURS) + def hour = hours - def days = Duration(n, DAYS) - def day = Duration(n, DAYS) + def days = durationIn(DAYS) + def day = days - def nanoseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS)) - def nanos[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS)) - def nanosecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS)) - def nano[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS)) + def nanoseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(nanoseconds) + def nanos[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = nanoseconds(c) + def nanosecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = nanoseconds(c) + def nano[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = nanoseconds(c) - def microseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS)) - def micros[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS)) - def microsecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS)) - def micro[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS)) + def microseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(microseconds) + def micros[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = microseconds(c) + def microsecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = microseconds(c) + def micro[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = microseconds(c) - def milliseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS)) - def millis[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS)) - def millisecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS)) - def milli[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS)) + def milliseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(milliseconds) + def millis[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = milliseconds(c) + def millisecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = milliseconds(c) + def milli[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = milliseconds(c) - def seconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, SECONDS)) - def second[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, SECONDS)) + def seconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(seconds) + def second[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = seconds(c) - def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MINUTES)) - def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MINUTES)) + def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(minutes) + def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = minutes(c) - def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, HOURS)) - def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, HOURS)) + def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(hours) + def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = hours(c) - def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, DAYS)) - def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, DAYS)) + def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(days) + def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = days(c) } -class DurationLong(n: Long) { - import duration.Classifier - - def nanoseconds = Duration(n, NANOSECONDS) - def nanos = Duration(n, NANOSECONDS) - def nanosecond = Duration(n, NANOSECONDS) - def nano = Duration(n, NANOSECONDS) - - def microseconds = Duration(n, MICROSECONDS) - def micros = Duration(n, MICROSECONDS) - def microsecond = Duration(n, MICROSECONDS) - def micro = Duration(n, MICROSECONDS) - - def milliseconds = Duration(n, MILLISECONDS) - def millis = Duration(n, MILLISECONDS) - def millisecond = Duration(n, MILLISECONDS) - def milli = Duration(n, MILLISECONDS) - - def seconds = Duration(n, SECONDS) - def second = Duration(n, SECONDS) - - def minutes = Duration(n, MINUTES) - def minute = Duration(n, MINUTES) - - def hours = Duration(n, HOURS) - def hour = Duration(n, HOURS) - - def days = Duration(n, DAYS) - def day = Duration(n, DAYS) - - def nanoseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS)) - def nanos[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS)) - def nanosecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS)) - def nano[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, NANOSECONDS)) - - def microseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS)) - def micros[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS)) - def microsecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS)) - def micro[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MICROSECONDS)) - - def milliseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS)) - def millis[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS)) - def millisecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS)) - def milli[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MILLISECONDS)) - - def seconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, SECONDS)) - def second[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, SECONDS)) - - def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MINUTES)) - def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, MINUTES)) - - def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, HOURS)) - def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, HOURS)) - - def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, DAYS)) - def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(n, DAYS)) +final class DurationInt(val n: Int) extends AnyVal with DurationConversions { + override protected def durationIn(unit: TimeUnit): FiniteDuration = Duration(n, unit) } -class DurationDouble(d: Double) { - import duration.Classifier - - def nanoseconds = Duration(d, NANOSECONDS) - def nanos = Duration(d, NANOSECONDS) - def nanosecond = Duration(d, NANOSECONDS) - def nano = Duration(d, NANOSECONDS) - - def microseconds = Duration(d, MICROSECONDS) - def micros = Duration(d, MICROSECONDS) - def microsecond = Duration(d, MICROSECONDS) - def micro = Duration(d, MICROSECONDS) - - def milliseconds = Duration(d, MILLISECONDS) - def millis = Duration(d, MILLISECONDS) - def millisecond = Duration(d, MILLISECONDS) - def milli = Duration(d, MILLISECONDS) - - def seconds = Duration(d, SECONDS) - def second = Duration(d, SECONDS) - - def minutes = Duration(d, MINUTES) - def minute = Duration(d, MINUTES) - - def hours = Duration(d, HOURS) - def hour = Duration(d, HOURS) - - def days = Duration(d, DAYS) - def day = Duration(d, DAYS) - - def nanoseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, NANOSECONDS)) - def nanos[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, NANOSECONDS)) - def nanosecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, NANOSECONDS)) - def nano[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, NANOSECONDS)) - - def microseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MICROSECONDS)) - def micros[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MICROSECONDS)) - def microsecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MICROSECONDS)) - def micro[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MICROSECONDS)) - - def milliseconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MILLISECONDS)) - def millis[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MILLISECONDS)) - def millisecond[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MILLISECONDS)) - def milli[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MILLISECONDS)) - - def seconds[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, SECONDS)) - def second[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, SECONDS)) - - def minutes[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MINUTES)) - def minute[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, MINUTES)) - - def hours[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, HOURS)) - def hour[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, HOURS)) +final class DurationLong(val n: Long) extends AnyVal with DurationConversions { + override protected def durationIn(unit: TimeUnit): FiniteDuration = Duration(n, unit) +} - def days[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, DAYS)) - def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, DAYS)) +final class DurationDouble(val d: Double) extends AnyVal with DurationConversions { + override protected def durationIn(unit: TimeUnit): FiniteDuration = Duration(d, unit) } diff --git a/test/files/jvm/duration-java.check b/test/files/jvm/duration-java.check new file mode 100644 index 0000000000..7ae257dcc0 --- /dev/null +++ b/test/files/jvm/duration-java.check @@ -0,0 +1,362 @@ + 0.0 nanoseconds => 0 days + 1.0 nanoseconds => 1 nanosecond + 7.0 nanoseconds => 7 nanoseconds + 10.0 nanoseconds => 10 nanoseconds + 12.0 nanoseconds => 12 nanoseconds + 24.0 nanoseconds => 24 nanoseconds + 30.0 nanoseconds => 30 nanoseconds + 49.0 nanoseconds => 49 nanoseconds + 60.0 nanoseconds => 60 nanoseconds + 70.0 nanoseconds => 70 nanoseconds + 84.0 nanoseconds => 84 nanoseconds + 100.0 nanoseconds => 100 nanoseconds + 120.0 nanoseconds => 120 nanoseconds + 144.0 nanoseconds => 144 nanoseconds + 168.0 nanoseconds => 168 nanoseconds + 210.0 nanoseconds => 210 nanoseconds + 240.0 nanoseconds => 240 nanoseconds + 288.0 nanoseconds => 288 nanoseconds + 300.0 nanoseconds => 300 nanoseconds + 360.0 nanoseconds => 360 nanoseconds + 420.0 nanoseconds => 420 nanoseconds + 576.0 nanoseconds => 576 nanoseconds + 600.0 nanoseconds => 600 nanoseconds + 700.0 nanoseconds => 700 nanoseconds + 720.0 nanoseconds => 720 nanoseconds + 900.0 nanoseconds => 900 nanoseconds + 1000.0 nanoseconds => 1 microsecond + 1200.0 nanoseconds => 1200 nanoseconds + 1440.0 nanoseconds => 1440 nanoseconds + 1800.0 nanoseconds => 1800 nanoseconds + 2400.0 nanoseconds => 2400 nanoseconds + 3000.0 nanoseconds => 3 microseconds + 3600.0 nanoseconds => 3600 nanoseconds + 6000.0 nanoseconds => 6 microseconds + 7000.0 nanoseconds => 7 microseconds + 10000.0 nanoseconds => 10 microseconds + 12000.0 nanoseconds => 12 microseconds + 24000.0 nanoseconds => 24 microseconds + 30000.0 nanoseconds => 30 microseconds + 60000.0 nanoseconds => 60 microseconds + 100000.0 nanoseconds => 100 microseconds + 1000000.0 nanoseconds => 1 millisecond + 7000000.0 nanoseconds => 7 milliseconds + 1.0E7 nanoseconds => 10 milliseconds + 1.2E7 nanoseconds => 12 milliseconds + 2.4E7 nanoseconds => 24 milliseconds + 3.0E7 nanoseconds => 30 milliseconds + 6.0E7 nanoseconds => 60 milliseconds + 1.0E8 nanoseconds => 100 milliseconds + 1.0E9 nanoseconds => 1 second + 1.0E12 nanoseconds => 1000 seconds + 0.0 microseconds => 0 days + 1.0 microseconds => 1 microsecond + 7.0 microseconds => 7 microseconds + 10.0 microseconds => 10 microseconds + 12.0 microseconds => 12 microseconds + 24.0 microseconds => 24 microseconds + 30.0 microseconds => 30 microseconds + 49.0 microseconds => 49 microseconds + 60.0 microseconds => 60 microseconds + 70.0 microseconds => 70 microseconds + 84.0 microseconds => 84 microseconds + 100.0 microseconds => 100 microseconds + 120.0 microseconds => 120 microseconds + 144.0 microseconds => 144 microseconds + 168.0 microseconds => 168 microseconds + 210.0 microseconds => 210 microseconds + 240.0 microseconds => 240 microseconds + 288.0 microseconds => 288 microseconds + 300.0 microseconds => 300 microseconds + 360.0 microseconds => 360 microseconds + 420.0 microseconds => 420 microseconds + 576.0 microseconds => 576 microseconds + 600.0 microseconds => 600 microseconds + 700.0 microseconds => 700 microseconds + 720.0 microseconds => 720 microseconds + 900.0 microseconds => 900 microseconds + 1000.0 microseconds => 1 millisecond + 1200.0 microseconds => 1200 microseconds + 1440.0 microseconds => 1440 microseconds + 1800.0 microseconds => 1800 microseconds + 2400.0 microseconds => 2400 microseconds + 3000.0 microseconds => 3 milliseconds + 3600.0 microseconds => 3600 microseconds + 6000.0 microseconds => 6 milliseconds + 7000.0 microseconds => 7 milliseconds + 10000.0 microseconds => 10 milliseconds + 12000.0 microseconds => 12 milliseconds + 24000.0 microseconds => 24 milliseconds + 30000.0 microseconds => 30 milliseconds + 60000.0 microseconds => 60 milliseconds + 100000.0 microseconds => 100 milliseconds + 1000000.0 microseconds => 1 second + 7000000.0 microseconds => 7 seconds + 1.0E7 microseconds => 10 seconds + 1.2E7 microseconds => 12 seconds + 2.4E7 microseconds => 24 seconds + 3.0E7 microseconds => 30 seconds + 6.0E7 microseconds => 1 minute + 1.0E8 microseconds => 100 seconds + 1.0E9 microseconds => 1000 seconds + 1.0E12 microseconds => 1000000 seconds + 0.0 milliseconds => 0 days + 1.0 milliseconds => 1 millisecond + 7.0 milliseconds => 7 milliseconds + 10.0 milliseconds => 10 milliseconds + 12.0 milliseconds => 12 milliseconds + 24.0 milliseconds => 24 milliseconds + 30.0 milliseconds => 30 milliseconds + 49.0 milliseconds => 49 milliseconds + 60.0 milliseconds => 60 milliseconds + 70.0 milliseconds => 70 milliseconds + 84.0 milliseconds => 84 milliseconds + 100.0 milliseconds => 100 milliseconds + 120.0 milliseconds => 120 milliseconds + 144.0 milliseconds => 144 milliseconds + 168.0 milliseconds => 168 milliseconds + 210.0 milliseconds => 210 milliseconds + 240.0 milliseconds => 240 milliseconds + 288.0 milliseconds => 288 milliseconds + 300.0 milliseconds => 300 milliseconds + 360.0 milliseconds => 360 milliseconds + 420.0 milliseconds => 420 milliseconds + 576.0 milliseconds => 576 milliseconds + 600.0 milliseconds => 600 milliseconds + 700.0 milliseconds => 700 milliseconds + 720.0 milliseconds => 720 milliseconds + 900.0 milliseconds => 900 milliseconds + 1000.0 milliseconds => 1 second + 1200.0 milliseconds => 1200 milliseconds + 1440.0 milliseconds => 1440 milliseconds + 1800.0 milliseconds => 1800 milliseconds + 2400.0 milliseconds => 2400 milliseconds + 3000.0 milliseconds => 3 seconds + 3600.0 milliseconds => 3600 milliseconds + 6000.0 milliseconds => 6 seconds + 7000.0 milliseconds => 7 seconds + 10000.0 milliseconds => 10 seconds + 12000.0 milliseconds => 12 seconds + 24000.0 milliseconds => 24 seconds + 30000.0 milliseconds => 30 seconds + 60000.0 milliseconds => 1 minute + 100000.0 milliseconds => 100 seconds + 1000000.0 milliseconds => 1000 seconds + 7000000.0 milliseconds => 7000 seconds + 1.0E7 milliseconds => 10000 seconds + 1.2E7 milliseconds => 200 minutes + 2.4E7 milliseconds => 400 minutes + 3.0E7 milliseconds => 500 minutes + 6.0E7 milliseconds => 1000 minutes + 1.0E8 milliseconds => 100000 seconds + 1.0E9 milliseconds => 1000000 seconds + 1.0E12 milliseconds => 1000000000 seconds + 0.0 seconds => 0 days + 1.0 seconds => 1 second + 7.0 seconds => 7 seconds + 10.0 seconds => 10 seconds + 12.0 seconds => 12 seconds + 24.0 seconds => 24 seconds + 30.0 seconds => 30 seconds + 49.0 seconds => 49 seconds + 60.0 seconds => 1 minute + 70.0 seconds => 70 seconds + 84.0 seconds => 84 seconds + 100.0 seconds => 100 seconds + 120.0 seconds => 2 minutes + 144.0 seconds => 144 seconds + 168.0 seconds => 168 seconds + 210.0 seconds => 210 seconds + 240.0 seconds => 4 minutes + 288.0 seconds => 288 seconds + 300.0 seconds => 5 minutes + 360.0 seconds => 6 minutes + 420.0 seconds => 7 minutes + 576.0 seconds => 576 seconds + 600.0 seconds => 10 minutes + 700.0 seconds => 700 seconds + 720.0 seconds => 12 minutes + 900.0 seconds => 15 minutes + 1000.0 seconds => 1000 seconds + 1200.0 seconds => 20 minutes + 1440.0 seconds => 24 minutes + 1800.0 seconds => 30 minutes + 2400.0 seconds => 40 minutes + 3000.0 seconds => 50 minutes + 3600.0 seconds => 1 hour + 6000.0 seconds => 100 minutes + 7000.0 seconds => 7000 seconds + 10000.0 seconds => 10000 seconds + 12000.0 seconds => 200 minutes + 24000.0 seconds => 400 minutes + 30000.0 seconds => 500 minutes + 60000.0 seconds => 1000 minutes + 100000.0 seconds => 100000 seconds + 1000000.0 seconds => 1000000 seconds + 7000000.0 seconds => 7000000 seconds + 1.0E7 seconds => 10000000 seconds + 1.2E7 seconds => 200000 minutes + 2.4E7 seconds => 400000 minutes + 3.0E7 seconds => 500000 minutes + 6.0E7 seconds => 1000000 minutes + 1.0E8 seconds => 100000000 seconds + 1.0E9 seconds => 1000000000 seconds + 1.0E12 seconds => 9223372036854775807 nanoseconds + 0.0 minutes => 0 days + 1.0 minutes => 1 minute + 7.0 minutes => 7 minutes + 10.0 minutes => 10 minutes + 12.0 minutes => 12 minutes + 24.0 minutes => 24 minutes + 30.0 minutes => 30 minutes + 49.0 minutes => 49 minutes + 60.0 minutes => 1 hour + 70.0 minutes => 70 minutes + 84.0 minutes => 84 minutes + 100.0 minutes => 100 minutes + 120.0 minutes => 2 hours + 144.0 minutes => 144 minutes + 168.0 minutes => 168 minutes + 210.0 minutes => 210 minutes + 240.0 minutes => 4 hours + 288.0 minutes => 288 minutes + 300.0 minutes => 5 hours + 360.0 minutes => 6 hours + 420.0 minutes => 7 hours + 576.0 minutes => 576 minutes + 600.0 minutes => 10 hours + 700.0 minutes => 700 minutes + 720.0 minutes => 12 hours + 900.0 minutes => 15 hours + 1000.0 minutes => 1000 minutes + 1200.0 minutes => 20 hours + 1440.0 minutes => 1 day + 1800.0 minutes => 30 hours + 2400.0 minutes => 40 hours + 3000.0 minutes => 50 hours + 3600.0 minutes => 60 hours + 6000.0 minutes => 100 hours + 7000.0 minutes => 7000 minutes + 10000.0 minutes => 10000 minutes + 12000.0 minutes => 200 hours + 24000.0 minutes => 400 hours + 30000.0 minutes => 500 hours + 60000.0 minutes => 1000 hours + 100000.0 minutes => 100000 minutes + 1000000.0 minutes => 1000000 minutes + 7000000.0 minutes => 7000000 minutes + 1.0E7 minutes => 10000000 minutes + 1.2E7 minutes => 200000 hours + 2.4E7 minutes => 400000 hours + 3.0E7 minutes => 500000 hours + 6.0E7 minutes => 1000000 hours + 1.0E8 minutes => 100000000 minutes + 1.0E9 minutes => 9223372036854775807 nanoseconds + 1.0E12 minutes => 9223372036854775807 nanoseconds + 0.0 hours => 0 days + 1.0 hours => 1 hour + 7.0 hours => 7 hours + 10.0 hours => 10 hours + 12.0 hours => 12 hours + 24.0 hours => 1 day + 30.0 hours => 30 hours + 49.0 hours => 49 hours + 60.0 hours => 60 hours + 70.0 hours => 70 hours + 84.0 hours => 84 hours + 100.0 hours => 100 hours + 120.0 hours => 5 days + 144.0 hours => 6 days + 168.0 hours => 7 days + 210.0 hours => 210 hours + 240.0 hours => 10 days + 288.0 hours => 12 days + 300.0 hours => 300 hours + 360.0 hours => 15 days + 420.0 hours => 420 hours + 576.0 hours => 24 days + 600.0 hours => 25 days + 700.0 hours => 700 hours + 720.0 hours => 30 days + 900.0 hours => 900 hours + 1000.0 hours => 1000 hours + 1200.0 hours => 50 days + 1440.0 hours => 60 days + 1800.0 hours => 75 days + 2400.0 hours => 100 days + 3000.0 hours => 125 days + 3600.0 hours => 150 days + 6000.0 hours => 250 days + 7000.0 hours => 7000 hours + 10000.0 hours => 10000 hours + 12000.0 hours => 500 days + 24000.0 hours => 1000 days + 30000.0 hours => 1250 days + 60000.0 hours => 2500 days + 100000.0 hours => 100000 hours + 1000000.0 hours => 1000000 hours + 7000000.0 hours => 9223372036854775807 nanoseconds + 1.0E7 hours => 9223372036854775807 nanoseconds + 1.2E7 hours => 9223372036854775807 nanoseconds + 2.4E7 hours => 9223372036854775807 nanoseconds + 3.0E7 hours => 9223372036854775807 nanoseconds + 6.0E7 hours => 9223372036854775807 nanoseconds + 1.0E8 hours => 9223372036854775807 nanoseconds + 1.0E9 hours => 9223372036854775807 nanoseconds + 1.0E12 hours => 9223372036854775807 nanoseconds + 0.0 days => 0 days + 1.0 days => 1 day + 7.0 days => 7 days + 10.0 days => 10 days + 12.0 days => 12 days + 24.0 days => 24 days + 30.0 days => 30 days + 49.0 days => 49 days + 60.0 days => 60 days + 70.0 days => 70 days + 84.0 days => 84 days + 100.0 days => 100 days + 120.0 days => 120 days + 144.0 days => 144 days + 168.0 days => 168 days + 210.0 days => 210 days + 240.0 days => 240 days + 288.0 days => 288 days + 300.0 days => 300 days + 360.0 days => 360 days + 420.0 days => 420 days + 576.0 days => 576 days + 600.0 days => 600 days + 700.0 days => 700 days + 720.0 days => 720 days + 900.0 days => 900 days + 1000.0 days => 1000 days + 1200.0 days => 1200 days + 1440.0 days => 1440 days + 1800.0 days => 1800 days + 2400.0 days => 2400 days + 3000.0 days => 3000 days + 3600.0 days => 3600 days + 6000.0 days => 6000 days + 7000.0 days => 7000 days + 10000.0 days => 10000 days + 12000.0 days => 12000 days + 24000.0 days => 24000 days + 30000.0 days => 30000 days + 60000.0 days => 60000 days + 100000.0 days => 100000 days + 1000000.0 days => 9223372036854775807 nanoseconds + 7000000.0 days => 9223372036854775807 nanoseconds + 1.0E7 days => 9223372036854775807 nanoseconds + 1.2E7 days => 9223372036854775807 nanoseconds + 2.4E7 days => 9223372036854775807 nanoseconds + 3.0E7 days => 9223372036854775807 nanoseconds + 6.0E7 days => 9223372036854775807 nanoseconds + 1.0E8 days => 9223372036854775807 nanoseconds + 1.0E9 days => 9223372036854775807 nanoseconds + 1.0E12 days => 9223372036854775807 nanoseconds + Inf => Duration.Inf + -Inf => Duration.MinusInf + +Inf => Duration.Inf + PlusInf => Duration.Inf + MinusInf => Duration.MinusInf diff --git a/test/files/jvm/duration-java/Test.java b/test/files/jvm/duration-java/Test.java new file mode 100644 index 0000000000..02feb522b8 --- /dev/null +++ b/test/files/jvm/duration-java/Test.java @@ -0,0 +1,38 @@ +import scala.concurrent.util.Duration; +import java.util.*; +import java.util.concurrent.TimeUnit; +import static java.util.concurrent.TimeUnit.*; + +public class Test { + public static List inputs = Arrays.asList(0d, 1d, 7d, 10d, 12d, 24d, 30d, 60d, 100d, 1000d, 1e6); + public static List makeNumbers() { + ArrayList xs = new ArrayList(); + for (Double n1: inputs) { + for (Double n2: inputs) { + Double n = n1 * n2; + if (!xs.contains(n)) + xs.add(n); + } + } + Double[] arr = xs.toArray(new Double[0]); + Arrays.sort(arr); + return Arrays.asList(arr); + } + + public static void p(Object x) { + System.out.println(x); + } + public static void main(String[] args) { + for (TimeUnit t : TimeUnit.values()) { + for (Double n: makeNumbers()) { + String s = "" + n + " " + t.toString().toLowerCase(); + Duration d = Duration.create(n, t); + p(String.format("%25s => %s", s, d)); + } + } + for (String s: Arrays.asList("Inf", "-Inf", "+Inf", "PlusInf", "MinusInf")) { + Duration d = Duration.create(s); + p(String.format("%25s => %s", s, d)); + } + } +} -- cgit v1.2.3