From a97297d7d253eb7573c995ce936f364b56d9bfe9 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 28 Apr 2016 22:43:46 -0700 Subject: Fields phase One step towards teasing apart the mixin phase, making each phase that adds members to traits responsible for mixing in those members into subclasses of said traits. Another design tenet is to not emit symbols or trees only to later remove them. Therefore, we model a val in a trait as its accessor. The underlying field is an implementation detail. It must be mixed into subclasses, but has no business in a trait (an interface). Also trying to reduce tree creation by changing less in subtrees during tree transforms. A lot of nice fixes fall out from this rework: - Correct bridges and more precise generic signatures for mixed in accessors, since they are now created before erasure. - Correct enclosing method attribute for classes nested in trait fields. Trait fields are now created as MethodSymbol (no longer TermSymbol). This symbol shows up in the `originalOwner` chain of a class declared within the field initializer. This promoted the field getter to being the enclosing method of the nested class, which it is not (the EnclosingMethod attribute is a source-level property). - Signature inference is now more similar between vals and defs - No more field for constant-typed vals, or mixed in accessors for subclasses. A constant val can be fully implemented in a trait. TODO: - give same treatment to trait lazy vals (only accessors, no fields) - remove support for presuper vals in traits (they don't have the right init semantics in traits anyway) - lambdalift should emit accessors for captured vals in traits, not a field Assorted notes from the full git history before squashing below. Unit-typed vals: don't suppress field it affects the memory model -- even a write of unit to a field is relevant... unit-typed lazy vals should never receive a field this need was unmasked by test/files/run/t7843-jsr223-service.scala, which no longer printed the output expected from the `0 to 10 foreach` Use getter.referenced to track traitsetter reify's toolbox compiler changes the name of the trait that owns the accessor between fields and constructors (`$` suffix), so that the trait setter cannot be found when doing mkAssign in constructors this could be solved by creating the mkAssign tree immediately during fields anyway, first experiment: use `referenced` now that fields runs closer to the constructors phase (I tried this before and something broke) Infer result type for `val`s, like we do for `def`s The lack of result type inference caused pos/t6780 to fail in the new field encoding for traits, as there is no separate accessor, and method synthesis computes the type signature based on the ValDef tree. This caused a cyclic error in implicit search, because now the implicit val's result type was not inferred from the super member, and inferring it from the RHS would cause implicit search to consider the member in question, so that a cycle is detected and type checking fails... Regardless of the new encoding, we should consistently infer result types for `def`s and `val`s. Removed test/files/run/t4287inferredMethodTypes.scala and test/files/presentation/t4287c, since they were relying on inferring argument types from "overridden" constructors in a test for range positions of default arguments. Constructors don't override, so that was a mis-feature of -Yinfer-argument-types. Had to slightly refactor test/files/presentation/doc, as it was relying on scalac inferring a big intersection type to approximate the anonymous class that's instantiated for `override lazy val analyzer`. Now that we infer `Global` as the expected type based on the overridden val, we make `getComment` private in navigating between good old Skylla and Charybdis. I'm not sure why we need this restriction for anonymous classes though; only structural calls are restricted in the way that we're trying to avoid. The old behavior is maintained nder -Xsource:2.11. Tests: - test/files/{pos,neg}/val_infer.scala - test/files/neg/val_sig_infer_match.scala - test/files/neg/val_sig_infer_struct.scala need NMT when inferring sig for accessor Q: why are we calling valDefSig and not methodSig? A: traits use defs for vals, but still use valDefSig... keep accessor and field info in synch --- test/files/neg/overloaded-unapply.check | 4 +- test/files/neg/t1960.check | 2 +- test/files/neg/t200.check | 4 +- test/files/neg/t2779.check | 4 +- test/files/neg/t278.check | 4 +- test/files/neg/t3871.check | 2 +- test/files/neg/t4541.check | 2 +- test/files/neg/t4541b.check | 2 +- test/files/neg/t5429.check | 2 +- test/files/neg/t591.check | 4 +- test/files/neg/t591.scala | 3 +- test/files/neg/t6335.check | 8 +- test/files/neg/t6446-additional.check | 31 +-- test/files/neg/t6446-missing.check | 29 +-- test/files/neg/t6446-show-phases.check | 29 +-- test/files/neg/t6666.check | 4 +- test/files/neg/t7494-no-options.check | 31 +-- test/files/neg/t7602.check | 4 +- test/files/neg/t7622-cyclic-dependency.check | 2 +- test/files/neg/t800.check | 12 +- test/files/neg/t8849.check | 2 +- test/files/neg/trait_fields_conflicts.check | 273 +++++++++++++++++++++ test/files/neg/trait_fields_conflicts.scala | 87 +++++++ .../neg/trait_fields_deprecated_overriding.check | 6 + .../neg/trait_fields_deprecated_overriding.flags | 1 + .../neg/trait_fields_deprecated_overriding.scala | 11 + test/files/neg/val_infer.check | 6 + test/files/neg/val_infer.scala | 4 + test/files/neg/val_sig_infer_match.check | 4 + test/files/neg/val_sig_infer_match.scala | 22 ++ test/files/neg/val_sig_infer_struct.check | 4 + test/files/neg/val_sig_infer_struct.scala | 8 + test/files/neg/warn-unused-privates.check | 7 +- test/files/pos/infer_override_def_args.flags | 1 + test/files/pos/infer_override_def_args.scala | 5 + .../pos/trait_fields_dependent_conflict.scala | 20 ++ test/files/pos/trait_fields_dependent_rebind.scala | 15 ++ .../pos/trait_fields_inherit_double_def.scala | 20 ++ test/files/pos/trait_fields_lambdalift.scala | 22 ++ .../pos/trait_fields_nested_private_object.scala | 8 + .../pos/trait_fields_nested_public_object.scala | 5 + test/files/pos/trait_fields_owners.scala | 19 ++ test/files/pos/trait_fields_private_this.scala | 5 + test/files/pos/trait_fields_static_fwd.scala | 10 + test/files/pos/val_infer.scala | 5 + test/files/presentation/doc/doc.scala | 2 +- test/files/presentation/scope-completion-3.check | 14 +- test/files/presentation/t4287c.check | 11 - test/files/presentation/t4287c.flags | 1 - test/files/presentation/t4287c/Test.scala | 3 - test/files/presentation/t4287c/src/Foo.scala | 9 - test/files/run/SymbolsTest.scala | 18 +- test/files/run/analyzerPlugins.check | 37 ++- test/files/run/programmatic-main.check | 29 +-- .../run/reflection-fieldsymbol-navigation.check | 6 +- test/files/run/repl-colon-type.check | 2 +- test/files/run/showdecl.check | 2 +- test/files/run/showdecl/Macros_1.scala | 2 +- test/files/run/showraw_mods.check | 2 +- test/files/run/t4287inferredMethodTypes.check | 30 --- test/files/run/t4287inferredMethodTypes.scala | 25 -- test/files/run/t6733.check | 15 +- test/files/run/t7533.check | 51 ++-- test/files/run/t7533.scala | 34 +-- test/files/run/t8549.scala | 6 +- test/files/run/trait_fields_bytecode.scala | 23 ++ test/files/run/trait_fields_final.scala | 21 ++ test/files/run/trait_fields_init.check | 21 ++ test/files/run/trait_fields_init.scala | 55 +++++ test/files/run/trait_fields_repl.check | 11 + test/files/run/trait_fields_repl.scala | 10 + .../run/trait_fields_three_layer_overrides.check | 2 + .../run/trait_fields_three_layer_overrides.scala | 25 ++ test/files/run/trait_fields_volatile.scala | 13 + 74 files changed, 945 insertions(+), 288 deletions(-) create mode 100644 test/files/neg/trait_fields_conflicts.check create mode 100644 test/files/neg/trait_fields_conflicts.scala create mode 100644 test/files/neg/trait_fields_deprecated_overriding.check create mode 100644 test/files/neg/trait_fields_deprecated_overriding.flags create mode 100644 test/files/neg/trait_fields_deprecated_overriding.scala create mode 100644 test/files/neg/val_infer.check create mode 100644 test/files/neg/val_infer.scala create mode 100644 test/files/neg/val_sig_infer_match.check create mode 100644 test/files/neg/val_sig_infer_match.scala create mode 100644 test/files/neg/val_sig_infer_struct.check create mode 100644 test/files/neg/val_sig_infer_struct.scala create mode 100644 test/files/pos/infer_override_def_args.flags create mode 100644 test/files/pos/infer_override_def_args.scala create mode 100644 test/files/pos/trait_fields_dependent_conflict.scala create mode 100644 test/files/pos/trait_fields_dependent_rebind.scala create mode 100644 test/files/pos/trait_fields_inherit_double_def.scala create mode 100644 test/files/pos/trait_fields_lambdalift.scala create mode 100644 test/files/pos/trait_fields_nested_private_object.scala create mode 100644 test/files/pos/trait_fields_nested_public_object.scala create mode 100644 test/files/pos/trait_fields_owners.scala create mode 100644 test/files/pos/trait_fields_private_this.scala create mode 100644 test/files/pos/trait_fields_static_fwd.scala create mode 100644 test/files/pos/val_infer.scala delete mode 100644 test/files/presentation/t4287c.check delete mode 100644 test/files/presentation/t4287c.flags delete mode 100644 test/files/presentation/t4287c/Test.scala delete mode 100644 test/files/presentation/t4287c/src/Foo.scala delete mode 100644 test/files/run/t4287inferredMethodTypes.check delete mode 100644 test/files/run/t4287inferredMethodTypes.scala create mode 100644 test/files/run/trait_fields_bytecode.scala create mode 100644 test/files/run/trait_fields_final.scala create mode 100644 test/files/run/trait_fields_init.check create mode 100644 test/files/run/trait_fields_init.scala create mode 100644 test/files/run/trait_fields_repl.check create mode 100644 test/files/run/trait_fields_repl.scala create mode 100644 test/files/run/trait_fields_three_layer_overrides.check create mode 100644 test/files/run/trait_fields_three_layer_overrides.scala create mode 100644 test/files/run/trait_fields_volatile.scala (limited to 'test/files') diff --git a/test/files/neg/overloaded-unapply.check b/test/files/neg/overloaded-unapply.check index 68a826bac2..3951166de5 100644 --- a/test/files/neg/overloaded-unapply.check +++ b/test/files/neg/overloaded-unapply.check @@ -7,8 +7,8 @@ match argument types (List[a]) overloaded-unapply.scala:22: error: cannot resolve overloaded unapply case List(x, xs) => 7 ^ -overloaded-unapply.scala:12: error: method unapply is defined twice - conflicting symbols both originated in file 'overloaded-unapply.scala' +overloaded-unapply.scala:12: error: method unapply is defined twice; + the conflicting method unapply was defined at line 7:7 def unapply[a](xs: List[a]): Option[Null] = xs match { ^ three errors found diff --git a/test/files/neg/t1960.check b/test/files/neg/t1960.check index 5238141c4e..de0907b4a9 100644 --- a/test/files/neg/t1960.check +++ b/test/files/neg/t1960.check @@ -1,4 +1,4 @@ -t1960.scala:5: error: parameter 'p' requires field but conflicts with method p in trait TBase +t1960.scala:5: error: parameter 'p' requires field but conflicts with variable p in trait TBase class Aclass (p: Int) extends TBase { def g() { f(p) } } ^ one error found diff --git a/test/files/neg/t200.check b/test/files/neg/t200.check index b6b1a32267..f0c5e77772 100644 --- a/test/files/neg/t200.check +++ b/test/files/neg/t200.check @@ -1,5 +1,5 @@ -t200.scala:7: error: method foo is defined twice - conflicting symbols both originated in file 't200.scala' +t200.scala:7: error: method foo is defined twice; + the conflicting method foo was defined at line 6:7 def foo: Int; ^ one error found diff --git a/test/files/neg/t2779.check b/test/files/neg/t2779.check index 0ab4c50d0f..9881d5182c 100644 --- a/test/files/neg/t2779.check +++ b/test/files/neg/t2779.check @@ -1,5 +1,5 @@ -t2779.scala:16: error: method f is defined twice - conflicting symbols both originated in file 't2779.scala' +t2779.scala:16: error: method f is defined twice; + the conflicting method f was defined at line 15:18 override def f = List(M1) ^ one error found diff --git a/test/files/neg/t278.check b/test/files/neg/t278.check index 405f7d225c..940b8edcef 100644 --- a/test/files/neg/t278.check +++ b/test/files/neg/t278.check @@ -4,8 +4,8 @@ t278.scala:5: error: overloaded method value a with alternatives: does not take type parameters println(a[A]) ^ -t278.scala:4: error: method a is defined twice - conflicting symbols both originated in file 't278.scala' +t278.scala:4: error: method a is defined twice; + the conflicting method a was defined at line 3:7 def a = (p:A) => () ^ two errors found diff --git a/test/files/neg/t3871.check b/test/files/neg/t3871.check index b920357ee6..c9667abfb6 100644 --- a/test/files/neg/t3871.check +++ b/test/files/neg/t3871.check @@ -1,5 +1,5 @@ t3871.scala:4: error: variable foo in class Sub2 cannot be accessed in Sub2 - Access to protected method foo not permitted because + Access to protected variable foo not permitted because enclosing class Base is not a subclass of class Sub2 where target is defined s.foo = true diff --git a/test/files/neg/t4541.check b/test/files/neg/t4541.check index 7bd8ff78f9..7ee0cc6414 100644 --- a/test/files/neg/t4541.check +++ b/test/files/neg/t4541.check @@ -1,5 +1,5 @@ t4541.scala:11: error: variable data in class Sparse cannot be accessed in Sparse[Int] - Access to protected method data not permitted because + Access to protected variable data not permitted because prefix type Sparse[Int] does not conform to class Sparse$mcI$sp where the access take place that.data diff --git a/test/files/neg/t4541b.check b/test/files/neg/t4541b.check index 8a52fd97f4..2aae95f6b9 100644 --- a/test/files/neg/t4541b.check +++ b/test/files/neg/t4541b.check @@ -1,5 +1,5 @@ t4541b.scala:13: error: variable data in class SparseArray cannot be accessed in SparseArray[Int] - Access to protected method data not permitted because + Access to protected variable data not permitted because prefix type SparseArray[Int] does not conform to class SparseArray$mcI$sp where the access take place use(that.data.clone) diff --git a/test/files/neg/t5429.check b/test/files/neg/t5429.check index 4350696bc8..fb2d9c2e47 100644 --- a/test/files/neg/t5429.check +++ b/test/files/neg/t5429.check @@ -134,7 +134,7 @@ t5429.scala:87: error: overriding value value in class A0 of type Any; lazy value value cannot override a concrete non-lazy value override lazy val value = 0 // fail (strict over lazy) ^ -t5429.scala:91: error: value oneArg overrides nothing. +t5429.scala:91: error: lazy value oneArg overrides nothing. Note: the super classes of class F0 contain the following, non final members named oneArg: def oneArg(x: String): Any override lazy val oneArg = 15 // fail diff --git a/test/files/neg/t591.check b/test/files/neg/t591.check index d33f6d7a2f..c0bade0814 100644 --- a/test/files/neg/t591.check +++ b/test/files/neg/t591.check @@ -1,5 +1,5 @@ -t591.scala:38: error: method input_= is defined twice - conflicting symbols both originated in file 't591.scala' +t591.scala:40: error: method input_= is defined twice; + the conflicting variable input was defined at line 35:18 def input_=(in : Input) = {} ^ one error found diff --git a/test/files/neg/t591.scala b/test/files/neg/t591.scala index 0f0b02395c..14fb256a69 100644 --- a/test/files/neg/t591.scala +++ b/test/files/neg/t591.scala @@ -35,7 +35,8 @@ trait BaseFlow extends BaseList { private var input : Input = _; private var output : Output = _; + // the error message is a bit confusing, as it points here, + // but the symbol it reports is `input`'s actual setter (the one we synthesized) def input_=(in : Input) = {} - } } diff --git a/test/files/neg/t6335.check b/test/files/neg/t6335.check index 1727a05eb2..d118440f75 100644 --- a/test/files/neg/t6335.check +++ b/test/files/neg/t6335.check @@ -1,9 +1,9 @@ -t6335.scala:6: error: method Z is defined twice - conflicting symbols both originated in file 't6335.scala' +t6335.scala:6: error: method Z is defined twice; + the conflicting method Z was defined at line 5:7 implicit class Z[A](val i: A) { def zz = i } ^ -t6335.scala:3: error: method X is defined twice - conflicting symbols both originated in file 't6335.scala' +t6335.scala:3: error: method X is defined twice; + the conflicting method X was defined at line 2:7 implicit class X(val x: Int) { def xx = x } ^ two errors found diff --git a/test/files/neg/t6446-additional.check b/test/files/neg/t6446-additional.check index e56a67b28b..45db63317c 100644 --- a/test/files/neg/t6446-additional.check +++ b/test/files/neg/t6446-additional.check @@ -10,18 +10,19 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - tailcalls 11 replace tail calls by jumps - specialize 12 @specialized-driven class and method specialization - explicitouter 13 this refs to outer pointers - erasure 14 erase types, add interfaces for traits - posterasure 15 clean up erased inline classes - lazyvals 16 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 17 move nested functions to top level - constructors 18 move field definitions into constructors - flatten 19 eliminate inner classes - mixin 20 mixin composition - cleanup 21 platform-specific cleanups, generate reflective calls - delambdafy 22 remove lambdas - jvm 23 generate JVM bytecode - ploogin 24 A sample phase that does so many things it's kind of hard... - terminal 25 the last phase during a compilation run + fields 11 synthesize accessors and fields + tailcalls 12 replace tail calls by jumps + specialize 13 @specialized-driven class and method specialization + explicitouter 14 this refs to outer pointers + erasure 15 erase types, add interfaces for traits + posterasure 16 clean up erased inline classes + lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs + lambdalift 18 move nested functions to top level + constructors 19 move field definitions into constructors + flatten 20 eliminate inner classes + mixin 21 mixin composition + cleanup 22 platform-specific cleanups, generate reflective calls + delambdafy 23 remove lambdas + jvm 24 generate JVM bytecode + ploogin 25 A sample phase that does so many things it's kind of hard... + terminal 26 the last phase during a compilation run diff --git a/test/files/neg/t6446-missing.check b/test/files/neg/t6446-missing.check index 15f0ceb6e3..04523d18e6 100644 --- a/test/files/neg/t6446-missing.check +++ b/test/files/neg/t6446-missing.check @@ -11,17 +11,18 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - tailcalls 11 replace tail calls by jumps - specialize 12 @specialized-driven class and method specialization - explicitouter 13 this refs to outer pointers - erasure 14 erase types, add interfaces for traits - posterasure 15 clean up erased inline classes - lazyvals 16 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 17 move nested functions to top level - constructors 18 move field definitions into constructors - flatten 19 eliminate inner classes - mixin 20 mixin composition - cleanup 21 platform-specific cleanups, generate reflective calls - delambdafy 22 remove lambdas - jvm 23 generate JVM bytecode - terminal 24 the last phase during a compilation run + fields 11 synthesize accessors and fields + tailcalls 12 replace tail calls by jumps + specialize 13 @specialized-driven class and method specialization + explicitouter 14 this refs to outer pointers + erasure 15 erase types, add interfaces for traits + posterasure 16 clean up erased inline classes + lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs + lambdalift 18 move nested functions to top level + constructors 19 move field definitions into constructors + flatten 20 eliminate inner classes + mixin 21 mixin composition + cleanup 22 platform-specific cleanups, generate reflective calls + delambdafy 23 remove lambdas + jvm 24 generate JVM bytecode + terminal 25 the last phase during a compilation run diff --git a/test/files/neg/t6446-show-phases.check b/test/files/neg/t6446-show-phases.check index 280a4f43d5..03f8273c17 100644 --- a/test/files/neg/t6446-show-phases.check +++ b/test/files/neg/t6446-show-phases.check @@ -10,17 +10,18 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - tailcalls 11 replace tail calls by jumps - specialize 12 @specialized-driven class and method specialization - explicitouter 13 this refs to outer pointers - erasure 14 erase types, add interfaces for traits - posterasure 15 clean up erased inline classes - lazyvals 16 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 17 move nested functions to top level - constructors 18 move field definitions into constructors - flatten 19 eliminate inner classes - mixin 20 mixin composition - cleanup 21 platform-specific cleanups, generate reflective calls - delambdafy 22 remove lambdas - jvm 23 generate JVM bytecode - terminal 24 the last phase during a compilation run + fields 11 synthesize accessors and fields + tailcalls 12 replace tail calls by jumps + specialize 13 @specialized-driven class and method specialization + explicitouter 14 this refs to outer pointers + erasure 15 erase types, add interfaces for traits + posterasure 16 clean up erased inline classes + lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs + lambdalift 18 move nested functions to top level + constructors 19 move field definitions into constructors + flatten 20 eliminate inner classes + mixin 21 mixin composition + cleanup 22 platform-specific cleanups, generate reflective calls + delambdafy 23 remove lambdas + jvm 24 generate JVM bytecode + terminal 25 the last phase during a compilation run diff --git a/test/files/neg/t6666.check b/test/files/neg/t6666.check index 43c8252753..090ef72770 100644 --- a/test/files/neg/t6666.check +++ b/test/files/neg/t6666.check @@ -1,7 +1,7 @@ t6666.scala:23: error: Implementation restriction: access of method x$2 in object O1 from <$anon: Function0>, would require illegal premature access to object O1 F.byname(x) ^ -t6666.scala:30: error: Implementation restriction: access of value x$3 in object O2 from <$anon: Function0>, would require illegal premature access to object O2 +t6666.scala:30: error: Implementation restriction: access of method x$3 in object O2 from <$anon: Function0>, would require illegal premature access to object O2 F.byname(x) ^ t6666.scala:37: error: Implementation restriction: access of method x$4 in object O3 from <$anon: Function0>, would require illegal premature access to object O3 @@ -10,7 +10,7 @@ t6666.scala:37: error: Implementation restriction: access of method x$4 in objec t6666.scala:50: error: Implementation restriction: access of method x$6 in class C1 from <$anon: Function0>, would require illegal premature access to the unconstructed `this` of class C1 F.byname(x) ^ -t6666.scala:54: error: Implementation restriction: access of value x$7 in class C2 from <$anon: Function0>, would require illegal premature access to the unconstructed `this` of class C2 +t6666.scala:54: error: Implementation restriction: access of method x$7 in class C2 from <$anon: Function0>, would require illegal premature access to the unconstructed `this` of class C2 F.byname(x) ^ t6666.scala:58: error: Implementation restriction: access of method x$8 in class C3 from <$anon: Function0>, would require illegal premature access to the unconstructed `this` of class C3 diff --git a/test/files/neg/t7494-no-options.check b/test/files/neg/t7494-no-options.check index a4c4a1ad5b..bb143e8644 100644 --- a/test/files/neg/t7494-no-options.check +++ b/test/files/neg/t7494-no-options.check @@ -11,18 +11,19 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - tailcalls 11 replace tail calls by jumps - specialize 12 @specialized-driven class and method specialization - explicitouter 13 this refs to outer pointers - erasure 14 erase types, add interfaces for traits - posterasure 15 clean up erased inline classes - lazyvals 16 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 17 move nested functions to top level - constructors 18 move field definitions into constructors - flatten 19 eliminate inner classes - mixin 20 mixin composition - cleanup 21 platform-specific cleanups, generate reflective calls - delambdafy 22 remove lambdas - jvm 23 generate JVM bytecode - ploogin 24 A sample phase that does so many things it's kind of hard... - terminal 25 the last phase during a compilation run + fields 11 synthesize accessors and fields + tailcalls 12 replace tail calls by jumps + specialize 13 @specialized-driven class and method specialization + explicitouter 14 this refs to outer pointers + erasure 15 erase types, add interfaces for traits + posterasure 16 clean up erased inline classes + lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs + lambdalift 18 move nested functions to top level + constructors 19 move field definitions into constructors + flatten 20 eliminate inner classes + mixin 21 mixin composition + cleanup 22 platform-specific cleanups, generate reflective calls + delambdafy 23 remove lambdas + jvm 24 generate JVM bytecode + ploogin 25 A sample phase that does so many things it's kind of hard... + terminal 26 the last phase during a compilation run diff --git a/test/files/neg/t7602.check b/test/files/neg/t7602.check index 5bb1450d7d..5ce3776790 100644 --- a/test/files/neg/t7602.check +++ b/test/files/neg/t7602.check @@ -1,5 +1,5 @@ -t7602.scala:16: error: method foo is defined twice - conflicting symbols both originated in file 't7602.scala' +t7602.scala:16: error: method foo is defined twice; + the conflicting method foo was defined at line 15:7 def foo : Device ^ one error found diff --git a/test/files/neg/t7622-cyclic-dependency.check b/test/files/neg/t7622-cyclic-dependency.check index 3546964f5f..81e3ecc6a4 100644 --- a/test/files/neg/t7622-cyclic-dependency.check +++ b/test/files/neg/t7622-cyclic-dependency.check @@ -1 +1 @@ -error: Cycle in phase dependencies detected at cyclicdependency1, created phase-cycle.dot +error: Cycle in phase dependencies detected at cyclicdependency2, created phase-cycle.dot diff --git a/test/files/neg/t800.check b/test/files/neg/t800.check index 8ba95fddde..238b8dd27d 100644 --- a/test/files/neg/t800.check +++ b/test/files/neg/t800.check @@ -1,16 +1,16 @@ t800.scala:4: error: qualification is already defined as value qualification val qualification = false; ^ -t800.scala:8: error: method qualification is defined twice - conflicting symbols both originated in file 't800.scala' +t800.scala:8: error: value qualification is defined twice; + the conflicting variable qualification was defined at line 7:7 val qualification = false; ^ -t800.scala:12: error: value qualification is defined twice - conflicting symbols both originated in file 't800.scala' +t800.scala:12: error: variable qualification is defined twice; + the conflicting value qualification was defined at line 11:7 var qualification = false; ^ -t800.scala:16: error: method qualification is defined twice - conflicting symbols both originated in file 't800.scala' +t800.scala:16: error: variable qualification is defined twice; + the conflicting variable qualification was defined at line 15:7 var qualification = false; ^ four errors found diff --git a/test/files/neg/t8849.check b/test/files/neg/t8849.check index 15b00aee8b..1d5b4164b2 100644 --- a/test/files/neg/t8849.check +++ b/test/files/neg/t8849.check @@ -1,5 +1,5 @@ t8849.scala:8: error: ambiguous implicit values: - both value global in object Implicits of type => scala.concurrent.ExecutionContext + both lazy value global in object Implicits of type => scala.concurrent.ExecutionContext and value dummy of type scala.concurrent.ExecutionContext match expected type scala.concurrent.ExecutionContext require(implicitly[ExecutionContext] eq dummy) diff --git a/test/files/neg/trait_fields_conflicts.check b/test/files/neg/trait_fields_conflicts.check new file mode 100644 index 0000000000..696d0284c1 --- /dev/null +++ b/test/files/neg/trait_fields_conflicts.check @@ -0,0 +1,273 @@ +trait_fields_conflicts.scala:5: error: overriding value x in trait Val of type Int; + value x needs `override' modifier +trait ValForVal extends Val { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:6: error: overriding value x in trait Val of type Int; + variable x needs `override' modifier +trait VarForVal extends Val { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:7: error: overriding value x in trait Val of type Int; + method x needs `override' modifier +trait DefForVal extends Val { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:8: error: overriding variable x in trait Var of type Int; + value x needs `override' modifier +trait ValForVar extends Var { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:9: error: overriding variable x in trait Var of type Int; + variable x needs `override' modifier +trait VarForVar extends Var { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:10: error: overriding variable x in trait Var of type Int; + method x needs `override' modifier +trait DefForVar extends Var { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:11: error: overriding lazy value x in trait Lazy of type Int; + value x needs `override' modifier +trait ValForLazy extends Lazy { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:12: error: overriding lazy value x in trait Lazy of type Int; + variable x needs `override' modifier +trait VarForLazy extends Lazy { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:13: error: overriding lazy value x in trait Lazy of type Int; + method x needs `override' modifier +trait DefForLazy extends Lazy { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:16: error: overriding value x in trait Val of type Int; + variable x needs to be a stable, immutable value +trait VarForValOvr extends Val { override var x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:17: error: overriding value x in trait Val of type Int; + method x needs to be a stable, immutable value +trait DefForValOvr extends Val { override def x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:18: error: overriding variable x in trait Var of type Int; + value x cannot override a mutable variable +trait ValForVarOvr extends Var { override val x: Int = 1 } // bad override -- unsound if used in path and var changes + ^ +trait_fields_conflicts.scala:19: error: overriding variable x in trait Var of type Int; + variable x cannot override a mutable variable +trait VarForVarOvr extends Var { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:20: error: overriding variable x in trait Var of type Int; + method x cannot override a mutable variable +trait DefForVarOvr extends Var { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:21: error: overriding lazy value x in trait Lazy of type Int; + value x must be declared lazy to override a concrete lazy value +trait ValForLazyOvr extends Lazy { override val x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:22: error: overriding lazy value x in trait Lazy of type Int; + variable x needs to be a stable, immutable value +trait VarForLazyOvr extends Lazy { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:23: error: overriding lazy value x in trait Lazy of type Int; + method x needs to be a stable, immutable value +trait DefForLazyOvr extends Lazy { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:25: error: overriding value x in trait Val of type Int; + value x needs `override' modifier +class CValForVal extends Val { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:26: error: overriding value x in trait Val of type Int; + variable x needs `override' modifier +class CVarForVal extends Val { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:27: error: overriding value x in trait Val of type Int; + method x needs `override' modifier +class CDefForVal extends Val { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:28: error: overriding variable x in trait Var of type Int; + value x needs `override' modifier +class CValForVar extends Var { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:29: error: overriding variable x in trait Var of type Int; + variable x needs `override' modifier +class CVarForVar extends Var { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:30: error: overriding variable x in trait Var of type Int; + method x needs `override' modifier +class CDefForVar extends Var { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:31: error: overriding lazy value x in trait Lazy of type Int; + value x needs `override' modifier +class CValForLazy extends Lazy { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:32: error: overriding lazy value x in trait Lazy of type Int; + variable x needs `override' modifier +class CVarForLazy extends Lazy { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:33: error: overriding lazy value x in trait Lazy of type Int; + method x needs `override' modifier +class CDefForLazy extends Lazy { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:36: error: overriding value x in trait Val of type Int; + variable x needs to be a stable, immutable value +class CVarForValOvr extends Val { override var x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:37: error: overriding value x in trait Val of type Int; + method x needs to be a stable, immutable value +class CDefForValOvr extends Val { override def x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:38: error: overriding variable x in trait Var of type Int; + value x cannot override a mutable variable +class CValForVarOvr extends Var { override val x: Int = 1 } // bad override -- unsound if used in path and var changes + ^ +trait_fields_conflicts.scala:39: error: overriding variable x in trait Var of type Int; + variable x cannot override a mutable variable +class CVarForVarOvr extends Var { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:40: error: overriding variable x in trait Var of type Int; + method x cannot override a mutable variable +class CDefForVarOvr extends Var { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:41: error: overriding lazy value x in trait Lazy of type Int; + value x must be declared lazy to override a concrete lazy value +class CValForLazyOvr extends Lazy { override val x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:42: error: overriding lazy value x in trait Lazy of type Int; + variable x needs to be a stable, immutable value +class CVarForLazyOvr extends Lazy { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:43: error: overriding lazy value x in trait Lazy of type Int; + method x needs to be a stable, immutable value +class CDefForLazyOvr extends Lazy { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:49: error: overriding value x in class CVal of type Int; + value x needs `override' modifier +trait ValForCVal extends CVal { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:50: error: overriding value x in class CVal of type Int; + variable x needs `override' modifier +trait VarForCVal extends CVal { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:51: error: overriding value x in class CVal of type Int; + method x needs `override' modifier +trait DefForCVal extends CVal { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:52: error: overriding variable x in class CVar of type Int; + value x needs `override' modifier +trait ValForCVar extends CVar { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:53: error: overriding variable x in class CVar of type Int; + variable x needs `override' modifier +trait VarForCVar extends CVar { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:54: error: overriding variable x in class CVar of type Int; + method x needs `override' modifier +trait DefForCVar extends CVar { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:55: error: overriding lazy value x in class CLazy of type Int; + value x needs `override' modifier +trait ValForCLazy extends CLazy { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:56: error: overriding lazy value x in class CLazy of type Int; + variable x needs `override' modifier +trait VarForCLazy extends CLazy { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:57: error: overriding lazy value x in class CLazy of type Int; + method x needs `override' modifier +trait DefForCLazy extends CLazy { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:60: error: overriding value x in class CVal of type Int; + variable x needs to be a stable, immutable value +trait VarForCValOvr extends CVal { override var x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:61: error: overriding value x in class CVal of type Int; + method x needs to be a stable, immutable value +trait DefForCValOvr extends CVal { override def x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:62: error: overriding variable x in class CVar of type Int; + value x cannot override a mutable variable +trait ValForCVarOvr extends CVar { override val x: Int = 1 } // bad override -- unsound if used in path and var changes + ^ +trait_fields_conflicts.scala:63: error: overriding variable x in class CVar of type Int; + variable x cannot override a mutable variable +trait VarForCVarOvr extends CVar { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:64: error: overriding variable x in class CVar of type Int; + method x cannot override a mutable variable +trait DefForCVarOvr extends CVar { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:65: error: overriding lazy value x in class CLazy of type Int; + value x must be declared lazy to override a concrete lazy value +trait ValForCLazyOvr extends CLazy { override val x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:66: error: overriding lazy value x in class CLazy of type Int; + variable x needs to be a stable, immutable value +trait VarForCLazyOvr extends CLazy { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:67: error: overriding lazy value x in class CLazy of type Int; + method x needs to be a stable, immutable value +trait DefForCLazyOvr extends CLazy { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:69: error: overriding value x in class CVal of type Int; + value x needs `override' modifier +class CValForCVal extends CVal { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:70: error: overriding value x in class CVal of type Int; + variable x needs `override' modifier +class CVarForCVal extends CVal { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:71: error: overriding value x in class CVal of type Int; + method x needs `override' modifier +class CDefForCVal extends CVal { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:72: error: overriding variable x in class CVar of type Int; + value x needs `override' modifier +class CValForCVar extends CVar { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:73: error: overriding variable x in class CVar of type Int; + variable x needs `override' modifier +class CVarForCVar extends CVar { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:74: error: overriding variable x in class CVar of type Int; + method x needs `override' modifier +class CDefForCVar extends CVar { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:75: error: overriding lazy value x in class CLazy of type Int; + value x needs `override' modifier +class CValForCLazy extends CLazy { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:76: error: overriding lazy value x in class CLazy of type Int; + variable x needs `override' modifier +class CVarForCLazy extends CLazy { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:77: error: overriding lazy value x in class CLazy of type Int; + method x needs `override' modifier +class CDefForCLazy extends CLazy { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:80: error: overriding value x in class CVal of type Int; + variable x needs to be a stable, immutable value +class CVarForCValOvr extends CVal { override var x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:81: error: overriding value x in class CVal of type Int; + method x needs to be a stable, immutable value +class CDefForCValOvr extends CVal { override def x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:82: error: overriding variable x in class CVar of type Int; + value x cannot override a mutable variable +class CValForCVarOvr extends CVar { override val x: Int = 1 } // bad override -- unsound if used in path and var changes + ^ +trait_fields_conflicts.scala:83: error: overriding variable x in class CVar of type Int; + variable x cannot override a mutable variable +class CVarForCVarOvr extends CVar { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:84: error: overriding variable x in class CVar of type Int; + method x cannot override a mutable variable +class CDefForCVarOvr extends CVar { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:85: error: overriding lazy value x in class CLazy of type Int; + value x must be declared lazy to override a concrete lazy value +class CValForCLazyOvr extends CLazy { override val x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:86: error: overriding lazy value x in class CLazy of type Int; + variable x needs to be a stable, immutable value +class CVarForCLazyOvr extends CLazy { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:87: error: overriding lazy value x in class CLazy of type Int; + method x needs to be a stable, immutable value +class CDefForCLazyOvr extends CLazy { override def x: Int = 1 } // bad override -- why? + ^ +68 errors found diff --git a/test/files/neg/trait_fields_conflicts.scala b/test/files/neg/trait_fields_conflicts.scala new file mode 100644 index 0000000000..92fc106e44 --- /dev/null +++ b/test/files/neg/trait_fields_conflicts.scala @@ -0,0 +1,87 @@ +trait Val { val x: Int = 123 } +trait Var { var x: Int = 123 } +trait Lazy { lazy val x: Int = 123 } + +trait ValForVal extends Val { val x: Int = 1 } // needs override +trait VarForVal extends Val { var x: Int = 1 } // needs override +trait DefForVal extends Val { def x: Int = 1 } // needs override +trait ValForVar extends Var { val x: Int = 1 } // needs override +trait VarForVar extends Var { var x: Int = 1 } // needs override +trait DefForVar extends Var { def x: Int = 1 } // needs override +trait ValForLazy extends Lazy { val x: Int = 1 } // needs override +trait VarForLazy extends Lazy { var x: Int = 1 } // needs override +trait DefForLazy extends Lazy { def x: Int = 1 } // needs override + +trait ValForValOvr extends Val { override val x: Int = 1 } // override ok +trait VarForValOvr extends Val { override var x: Int = 1 } // bad override +trait DefForValOvr extends Val { override def x: Int = 1 } // bad override +trait ValForVarOvr extends Var { override val x: Int = 1 } // bad override -- unsound if used in path and var changes +trait VarForVarOvr extends Var { override var x: Int = 1 } // bad override -- why? +trait DefForVarOvr extends Var { override def x: Int = 1 } // bad override -- why? +trait ValForLazyOvr extends Lazy { override val x: Int = 1 } // bad override -- why? +trait VarForLazyOvr extends Lazy { override var x: Int = 1 } // bad override -- why? +trait DefForLazyOvr extends Lazy { override def x: Int = 1 } // bad override -- why? + +class CValForVal extends Val { val x: Int = 1 } // needs override +class CVarForVal extends Val { var x: Int = 1 } // needs override +class CDefForVal extends Val { def x: Int = 1 } // needs override +class CValForVar extends Var { val x: Int = 1 } // needs override +class CVarForVar extends Var { var x: Int = 1 } // needs override +class CDefForVar extends Var { def x: Int = 1 } // needs override +class CValForLazy extends Lazy { val x: Int = 1 } // needs override +class CVarForLazy extends Lazy { var x: Int = 1 } // needs override +class CDefForLazy extends Lazy { def x: Int = 1 } // needs override + +class CValForValOvr extends Val { override val x: Int = 1 } // override ok +class CVarForValOvr extends Val { override var x: Int = 1 } // bad override +class CDefForValOvr extends Val { override def x: Int = 1 } // bad override +class CValForVarOvr extends Var { override val x: Int = 1 } // bad override -- unsound if used in path and var changes +class CVarForVarOvr extends Var { override var x: Int = 1 } // bad override -- why? +class CDefForVarOvr extends Var { override def x: Int = 1 } // bad override -- why? +class CValForLazyOvr extends Lazy { override val x: Int = 1 } // bad override -- why? +class CVarForLazyOvr extends Lazy { override var x: Int = 1 } // bad override -- why? +class CDefForLazyOvr extends Lazy { override def x: Int = 1 } // bad override -- why? + +class CVal { val x: Int = 123 } +class CVar { var x: Int = 123 } +class CLazy { lazy val x: Int = 123 } + +trait ValForCVal extends CVal { val x: Int = 1 } // needs override +trait VarForCVal extends CVal { var x: Int = 1 } // needs override +trait DefForCVal extends CVal { def x: Int = 1 } // needs override +trait ValForCVar extends CVar { val x: Int = 1 } // needs override +trait VarForCVar extends CVar { var x: Int = 1 } // needs override +trait DefForCVar extends CVar { def x: Int = 1 } // needs override +trait ValForCLazy extends CLazy { val x: Int = 1 } // needs override +trait VarForCLazy extends CLazy { var x: Int = 1 } // needs override +trait DefForCLazy extends CLazy { def x: Int = 1 } // needs override + +trait ValForCValOvr extends CVal { override val x: Int = 1 } // override ok +trait VarForCValOvr extends CVal { override var x: Int = 1 } // bad override +trait DefForCValOvr extends CVal { override def x: Int = 1 } // bad override +trait ValForCVarOvr extends CVar { override val x: Int = 1 } // bad override -- unsound if used in path and var changes +trait VarForCVarOvr extends CVar { override var x: Int = 1 } // bad override -- why? +trait DefForCVarOvr extends CVar { override def x: Int = 1 } // bad override -- why? +trait ValForCLazyOvr extends CLazy { override val x: Int = 1 } // bad override -- why? +trait VarForCLazyOvr extends CLazy { override var x: Int = 1 } // bad override -- why? +trait DefForCLazyOvr extends CLazy { override def x: Int = 1 } // bad override -- why? + +class CValForCVal extends CVal { val x: Int = 1 } // needs override +class CVarForCVal extends CVal { var x: Int = 1 } // needs override +class CDefForCVal extends CVal { def x: Int = 1 } // needs override +class CValForCVar extends CVar { val x: Int = 1 } // needs override +class CVarForCVar extends CVar { var x: Int = 1 } // needs override +class CDefForCVar extends CVar { def x: Int = 1 } // needs override +class CValForCLazy extends CLazy { val x: Int = 1 } // needs override +class CVarForCLazy extends CLazy { var x: Int = 1 } // needs override +class CDefForCLazy extends CLazy { def x: Int = 1 } // needs override + +class CValForCValOvr extends CVal { override val x: Int = 1 } // override ok +class CVarForCValOvr extends CVal { override var x: Int = 1 } // bad override +class CDefForCValOvr extends CVal { override def x: Int = 1 } // bad override +class CValForCVarOvr extends CVar { override val x: Int = 1 } // bad override -- unsound if used in path and var changes +class CVarForCVarOvr extends CVar { override var x: Int = 1 } // bad override -- why? +class CDefForCVarOvr extends CVar { override def x: Int = 1 } // bad override -- why? +class CValForCLazyOvr extends CLazy { override val x: Int = 1 } // bad override -- why? +class CVarForCLazyOvr extends CLazy { override var x: Int = 1 } // bad override -- why? +class CDefForCLazyOvr extends CLazy { override def x: Int = 1 } // bad override -- why? diff --git a/test/files/neg/trait_fields_deprecated_overriding.check b/test/files/neg/trait_fields_deprecated_overriding.check new file mode 100644 index 0000000000..89dfa5c295 --- /dev/null +++ b/test/files/neg/trait_fields_deprecated_overriding.check @@ -0,0 +1,6 @@ +trait_fields_deprecated_overriding.scala:8: warning: overriding value x in trait DeprecatedOverriding is deprecated + override val x = 2 + ^ +error: No warnings can be incurred under -Xfatal-warnings. +one warning found +one error found diff --git a/test/files/neg/trait_fields_deprecated_overriding.flags b/test/files/neg/trait_fields_deprecated_overriding.flags new file mode 100644 index 0000000000..c6bfaf1f64 --- /dev/null +++ b/test/files/neg/trait_fields_deprecated_overriding.flags @@ -0,0 +1 @@ +-deprecation -Xfatal-warnings diff --git a/test/files/neg/trait_fields_deprecated_overriding.scala b/test/files/neg/trait_fields_deprecated_overriding.scala new file mode 100644 index 0000000000..e7d722c92f --- /dev/null +++ b/test/files/neg/trait_fields_deprecated_overriding.scala @@ -0,0 +1,11 @@ +package scala + +trait DeprecatedOverriding { + @deprecatedOverriding val x = 1 +} + +class COverride extends DeprecatedOverriding { + override val x = 2 +} + +class CSynthImpl extends DeprecatedOverriding \ No newline at end of file diff --git a/test/files/neg/val_infer.check b/test/files/neg/val_infer.check new file mode 100644 index 0000000000..711450add9 --- /dev/null +++ b/test/files/neg/val_infer.check @@ -0,0 +1,6 @@ +val_infer.scala:3: error: type mismatch; + found : String("") + required: Int + trait Sub extends Base { def foo = "" } + ^ +one error found diff --git a/test/files/neg/val_infer.scala b/test/files/neg/val_infer.scala new file mode 100644 index 0000000000..7fe8393749 --- /dev/null +++ b/test/files/neg/val_infer.scala @@ -0,0 +1,4 @@ +class Test { + trait Base { def foo: Int } + trait Sub extends Base { def foo = "" } +} \ No newline at end of file diff --git a/test/files/neg/val_sig_infer_match.check b/test/files/neg/val_sig_infer_match.check new file mode 100644 index 0000000000..704c99cf84 --- /dev/null +++ b/test/files/neg/val_sig_infer_match.check @@ -0,0 +1,4 @@ +val_sig_infer_match.scala:21: error: value y is not a member of A + def m = f.y // doesn't compile anymore + ^ +one error found diff --git a/test/files/neg/val_sig_infer_match.scala b/test/files/neg/val_sig_infer_match.scala new file mode 100644 index 0000000000..fb8aa66d56 --- /dev/null +++ b/test/files/neg/val_sig_infer_match.scala @@ -0,0 +1,22 @@ +class A + +class B extends A { + def y: Int = 0 +} + +class B1 extends B +class B2 extends B + +class C { + def f: A = null +} + +class D extends C { + def s = "" + override final val f = s match { + case "" => new B1 + case _ => new B2 + } + + def m = f.y // doesn't compile anymore +} \ No newline at end of file diff --git a/test/files/neg/val_sig_infer_struct.check b/test/files/neg/val_sig_infer_struct.check new file mode 100644 index 0000000000..26efbbc3f4 --- /dev/null +++ b/test/files/neg/val_sig_infer_struct.check @@ -0,0 +1,4 @@ +val_sig_infer_struct.scala:7: error: value foo is not a member of Object + def bar = f.foo + ^ +one error found diff --git a/test/files/neg/val_sig_infer_struct.scala b/test/files/neg/val_sig_infer_struct.scala new file mode 100644 index 0000000000..e88340337c --- /dev/null +++ b/test/files/neg/val_sig_infer_struct.scala @@ -0,0 +1,8 @@ +class C { + def f: Object = this +} + +class D extends C { + override val f = new Object { def foo = 1 } + def bar = f.foo +} \ No newline at end of file diff --git a/test/files/neg/warn-unused-privates.check b/test/files/neg/warn-unused-privates.check index 4876ed8fc2..2e93f338bb 100644 --- a/test/files/neg/warn-unused-privates.check +++ b/test/files/neg/warn-unused-privates.check @@ -19,10 +19,7 @@ warn-unused-privates.scala:36: warning: private val in class Boppy is never used warn-unused-privates.scala:43: warning: private var in trait Accessors is never used private var v1: Int = 0 // warn ^ -warn-unused-privates.scala:43: warning: private setter in trait Accessors is never used - private var v1: Int = 0 // warn - ^ -warn-unused-privates.scala:44: warning: private setter in trait Accessors is never used +warn-unused-privates.scala:44: warning: private var in trait Accessors is never used private var v2: Int = 0 // warn, never set ^ warn-unused-privates.scala:45: warning: private var in trait Accessors is never used @@ -65,5 +62,5 @@ warn-unused-privates.scala:103: warning: local type OtherThing is never used type OtherThing = String // warn ^ error: No warnings can be incurred under -Xfatal-warnings. -22 warnings found +21 warnings found one error found diff --git a/test/files/pos/infer_override_def_args.flags b/test/files/pos/infer_override_def_args.flags new file mode 100644 index 0000000000..d1a8244169 --- /dev/null +++ b/test/files/pos/infer_override_def_args.flags @@ -0,0 +1 @@ +-Yinfer-argument-types \ No newline at end of file diff --git a/test/files/pos/infer_override_def_args.scala b/test/files/pos/infer_override_def_args.scala new file mode 100644 index 0000000000..ac10720c81 --- /dev/null +++ b/test/files/pos/infer_override_def_args.scala @@ -0,0 +1,5 @@ +abstract class A { def foo(a: Int): A } +class B extends A { + implicit def spackle(x: Int): A = new B + def foo(a) = a +} \ No newline at end of file diff --git a/test/files/pos/trait_fields_dependent_conflict.scala b/test/files/pos/trait_fields_dependent_conflict.scala new file mode 100644 index 0000000000..afb6f4b0c5 --- /dev/null +++ b/test/files/pos/trait_fields_dependent_conflict.scala @@ -0,0 +1,20 @@ +// derived from test/files/pos/S5.scala + +// compile with -uniqid to see a hint of the trouble +trait N { + // the symbol for self does not get rebound when synthesizing members in C + val self: N = ??? + val n: self.type = self +} + +abstract class M { + val self: N + val n: self.type +} + +abstract class MConflict extends N { + val self: N + val n: self.type +} + +class C extends M with N diff --git a/test/files/pos/trait_fields_dependent_rebind.scala b/test/files/pos/trait_fields_dependent_rebind.scala new file mode 100644 index 0000000000..e2cf4c43c3 --- /dev/null +++ b/test/files/pos/trait_fields_dependent_rebind.scala @@ -0,0 +1,15 @@ +// derived from test/files/pos/S5.scala + +// compile with -uniqid to see a hint of the trouble +trait N { + // the symbol for self does not get rebound when synthesizing members in C + val self: N = ??? + val n: self.type = self +} + +abstract class M { + val self: N + val n: self.type +} + +class C extends M with N diff --git a/test/files/pos/trait_fields_inherit_double_def.scala b/test/files/pos/trait_fields_inherit_double_def.scala new file mode 100644 index 0000000000..8703d6312c --- /dev/null +++ b/test/files/pos/trait_fields_inherit_double_def.scala @@ -0,0 +1,20 @@ +// done +// test/files/trait-defaults/fields.scala:24: error: double definition: +// def signalDelegate_=(x$1: Signalling): Unit at line 24 and +// def signalDelegate_=(x$1: Signalling): Unit at line 24 +// have same type +// class SUB extends IterableSplitter +// ^ +// one error found + +trait Signalling + +trait DelegatedSignalling extends Signalling { + var signalDelegate: Signalling +} + +trait IterableSplitter extends DelegatedSignalling { + var signalDelegate: Signalling = ??? +} + +class SUB extends IterableSplitter \ No newline at end of file diff --git a/test/files/pos/trait_fields_lambdalift.scala b/test/files/pos/trait_fields_lambdalift.scala new file mode 100644 index 0000000000..62304a5268 --- /dev/null +++ b/test/files/pos/trait_fields_lambdalift.scala @@ -0,0 +1,22 @@ +class Lift { + def foo = { + // this will be captured by the MouseHandler trait, + // which gives rise to a new trait field during LambdaLift + var Clicked = "Clicked" + + def bar = Clicked + + trait MouseHandler { + def mouseClicked = Clicked + bar + } + + class CC extends MouseHandler + + // new C {} + (new CC).mouseClicked + } +} + +object O extends Lift with App { + println(foo) +} diff --git a/test/files/pos/trait_fields_nested_private_object.scala b/test/files/pos/trait_fields_nested_private_object.scala new file mode 100644 index 0000000000..8efc1cb3fa --- /dev/null +++ b/test/files/pos/trait_fields_nested_private_object.scala @@ -0,0 +1,8 @@ +trait NestedObj { + private object O { println("NO") } +} + + +class C extends NestedObj { + def O = ??? +} \ No newline at end of file diff --git a/test/files/pos/trait_fields_nested_public_object.scala b/test/files/pos/trait_fields_nested_public_object.scala new file mode 100644 index 0000000000..016487fb8a --- /dev/null +++ b/test/files/pos/trait_fields_nested_public_object.scala @@ -0,0 +1,5 @@ +trait NestedObj { + object O { println("NO") } +} + +class C extends NestedObj \ No newline at end of file diff --git a/test/files/pos/trait_fields_owners.scala b/test/files/pos/trait_fields_owners.scala new file mode 100644 index 0000000000..6aa5572171 --- /dev/null +++ b/test/files/pos/trait_fields_owners.scala @@ -0,0 +1,19 @@ +trait V { + // ok + // error: java.lang.IllegalArgumentException: Could not find proxy for val f: Function1 in List(value f, value v, trait V, package , package ) (currentOwner= value ) + val v = { val f = (x: Int) => x + 1; f(2) } + + // ok + // assertion failed: + // Trying to access the this of another class: tree.symbol = trait V, class symbol = object V$class compilation unit: fields.scala + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + def scalaPropOrNone(name: String): Option[String] = ??? +} + +object O extends V \ No newline at end of file diff --git a/test/files/pos/trait_fields_private_this.scala b/test/files/pos/trait_fields_private_this.scala new file mode 100644 index 0000000000..8065cc89e6 --- /dev/null +++ b/test/files/pos/trait_fields_private_this.scala @@ -0,0 +1,5 @@ +trait Chars { + private[this] val char2uescapeArray: String = ??? +} + +object Chars extends Chars \ No newline at end of file diff --git a/test/files/pos/trait_fields_static_fwd.scala b/test/files/pos/trait_fields_static_fwd.scala new file mode 100644 index 0000000000..af2cdad9ff --- /dev/null +++ b/test/files/pos/trait_fields_static_fwd.scala @@ -0,0 +1,10 @@ +trait T { + // Need to mark the synthesized member in the object's module class as notPROTECTED, + // since the trait member will receive this flag later. + // If we don't add notPROTECTED to the synthesized one, the member will not be seen as overriding the trait member. + // Therefore, addForwarders's call to membersBasedOnFlags would see the deferred member in the trait, + // instead of the concrete (desired) one in the class, and thus not create the static forwarder. + protected val propFilename: String = "/" +} + +object P extends T diff --git a/test/files/pos/val_infer.scala b/test/files/pos/val_infer.scala new file mode 100644 index 0000000000..5f82da8393 --- /dev/null +++ b/test/files/pos/val_infer.scala @@ -0,0 +1,5 @@ +class Test { + implicit def s2i(s: String): Int = s.length + trait Base { def foo: Int } + trait Sub extends Base { val foo = "" } +} diff --git a/test/files/presentation/doc/doc.scala b/test/files/presentation/doc/doc.scala index 8c60af557b..08c6ebf059 100644 --- a/test/files/presentation/doc/doc.scala +++ b/test/files/presentation/doc/doc.scala @@ -37,7 +37,7 @@ object Test extends InteractiveTest { prepre + docComment(nTags) + prepost + post } - override lazy val compiler = { + override lazy val compiler: Global { def getComment(sym: Symbol, source: SourceFile, fragments: List[(Symbol,SourceFile)]): Option[Comment] } = { prepareSettings(settings) new Global(settings, compilerReporter) with MemberLookupBase with CommentFactoryBase with doc.ScaladocGlobalTrait { outer => diff --git a/test/files/presentation/scope-completion-3.check b/test/files/presentation/scope-completion-3.check index b70a7d5c6b..f2510127fb 100644 --- a/test/files/presentation/scope-completion-3.check +++ b/test/files/presentation/scope-completion-3.check @@ -3,7 +3,7 @@ reload: Completions.scala askScopeCompletion at Completions.scala(75,2) ================================================================================ [response] askScopeCompletion at (75,2) -retrieved 37 members +retrieved 38 members abstract class Base1 extends AnyRef abstract trait Trait1 extends AnyRef class Cb1 extends AnyRef @@ -14,6 +14,8 @@ def (): test.Completion1 def fb1: Int def fc1: Int def ft1: Int +def rt1: Int +def rt1_=(x$1: Int): Unit object Completion2 object Ob1 object Oc1 @@ -30,23 +32,22 @@ private[this] val vb1: Int private[this] val vb3: Int private[this] val vc1: Int private[this] val vc2: Int -private[this] val vt1: Int private[this] val vt3: Int private[this] var rb1: Int private[this] var rb3: Int private[this] var rc1: Int private[this] var rc2: Int -private[this] var rt1: Int private[this] var rt3: Int type tb1 = Completion1.this.tb1 type tc1 = Completion1.this.tc1 type tt1 = Completion1.this.tt1 +val vt1: Int ================================================================================ askScopeCompletion at Completions.scala(104,2) ================================================================================ [response] askScopeCompletion at (104,2) -retrieved 37 members +retrieved 38 members abstract class Base1 extends AnyRef abstract trait Trait1 extends AnyRef class Cb1 extends AnyRef @@ -57,6 +58,8 @@ def (): test.Completion2.type def fb1: Int def fo1: Int def ft1: Int +def rt1: Int +def rt1_=(x$1: Int): Unit object Completion2 object Ob1 object Oo1 @@ -73,15 +76,14 @@ private[this] val vb1: Int private[this] val vb3: Int private[this] val vo1: Int private[this] val vo2: Int -private[this] val vt1: Int private[this] val vt3: Int private[this] var rb1: Int private[this] var rb3: Int private[this] var ro1: Int private[this] var ro2: Int -private[this] var rt1: Int private[this] var rt3: Int type tb1 = test.Completion2.tb1 type to1 = test.Completion2.to1 type tt1 = test.Completion2.tt1 +val vt1: Int ================================================================================ diff --git a/test/files/presentation/t4287c.check b/test/files/presentation/t4287c.check deleted file mode 100644 index 42fc30997d..0000000000 --- a/test/files/presentation/t4287c.check +++ /dev/null @@ -1,11 +0,0 @@ -reload: Foo.scala - -askHyperlinkPos for `A` at (1,18) Foo.scala -================================================================================ -[response] found askHyperlinkPos for `A` at (3,8) Foo.scala -================================================================================ - -askHyperlinkPos for `a` at (1,25) Foo.scala -================================================================================ -[response] found askHyperlinkPos for `a` at (4,7) Foo.scala -================================================================================ diff --git a/test/files/presentation/t4287c.flags b/test/files/presentation/t4287c.flags deleted file mode 100644 index d1a8244169..0000000000 --- a/test/files/presentation/t4287c.flags +++ /dev/null @@ -1 +0,0 @@ --Yinfer-argument-types \ No newline at end of file diff --git a/test/files/presentation/t4287c/Test.scala b/test/files/presentation/t4287c/Test.scala deleted file mode 100644 index bec1131c4c..0000000000 --- a/test/files/presentation/t4287c/Test.scala +++ /dev/null @@ -1,3 +0,0 @@ -import scala.tools.nsc.interactive.tests.InteractiveTest - -object Test extends InteractiveTest \ No newline at end of file diff --git a/test/files/presentation/t4287c/src/Foo.scala b/test/files/presentation/t4287c/src/Foo.scala deleted file mode 100644 index 26870b5021..0000000000 --- a/test/files/presentation/t4287c/src/Foo.scala +++ /dev/null @@ -1,9 +0,0 @@ -class A(a: Int = A/*#*/.a/*#*/) - -object A { - val a = 2 -} - -class B extends A { - def this(a) = this() -} \ No newline at end of file diff --git a/test/files/run/SymbolsTest.scala b/test/files/run/SymbolsTest.scala index d5948ea168..7c185b0e09 100644 --- a/test/files/run/SymbolsTest.scala +++ b/test/files/run/SymbolsTest.scala @@ -137,16 +137,16 @@ object Test { // } // val an2 = () => { // object nested { - // val m = 'mfsa + // val m = 'mfsa // } // nested.m // } // val an3 = () => { // object nested { - // val f = () => { - // 'layered - // } - // def gets = f() + // val f = () => { + // 'layered + // } + // def gets = f() // } // nested.gets // } @@ -204,8 +204,8 @@ object Test { val s1 = 's1 def s2 = 's2 object inner { - val s3 = 's3 - val s4 = 's4 + val s3 = 's3 + val s4 = 's4 } } @@ -223,8 +223,8 @@ object Test { val s5 = 's5 def s6 = 's6 object inner2 { - val s7 = 's7 - def s8 = 's8 + val s7 = 's7 + def s8 = 's8 } } assert(Local.s5 == 's5) diff --git a/test/files/run/analyzerPlugins.check b/test/files/run/analyzerPlugins.check index 1bb7c6ceab..ca0005ea4d 100644 --- a/test/files/run/analyzerPlugins.check +++ b/test/files/run/analyzerPlugins.check @@ -21,7 +21,6 @@ lub(List(Int @testAnn, Int)) [1] pluginsPt(?, Trees$Annotated) [7] pluginsPt(?, Trees$Apply) [11] pluginsPt(?, Trees$ApplyImplicitView) [2] -pluginsPt(?, Trees$Assign) [7] pluginsPt(?, Trees$Block) [4] pluginsPt(?, Trees$ClassDef) [2] pluginsPt(?, Trees$DefDef) [14] @@ -31,9 +30,9 @@ pluginsPt(?, Trees$Literal) [16] pluginsPt(?, Trees$New) [5] pluginsPt(?, Trees$PackageDef) [1] pluginsPt(?, Trees$Return) [1] -pluginsPt(?, Trees$Select) [50] +pluginsPt(?, Trees$Select) [43] pluginsPt(?, Trees$Super) [2] -pluginsPt(?, Trees$This) [20] +pluginsPt(?, Trees$This) [13] pluginsPt(?, Trees$TypeApply) [3] pluginsPt(?, Trees$TypeBoundsTree) [2] pluginsPt(?, Trees$TypeDef) [1] @@ -47,23 +46,19 @@ pluginsPt(Boolean @testAnn, Trees$Literal) [1] pluginsPt(Boolean @testAnn, Trees$Select) [1] pluginsPt(Boolean, Trees$Apply) [1] pluginsPt(Boolean, Trees$Ident) [1] -pluginsPt(Boolean, Trees$Literal) [1] pluginsPt(Double, Trees$Select) [1] pluginsPt(Int @testAnn, Trees$Literal) [1] pluginsPt(Int, Trees$Apply) [1] -pluginsPt(Int, Trees$Ident) [2] -pluginsPt(Int, Trees$If) [1] -pluginsPt(Int, Trees$Literal) [5] +pluginsPt(Int, Trees$Ident) [1] +pluginsPt(Int, Trees$Literal) [4] pluginsPt(Int, Trees$Select) [3] -pluginsPt(List, Trees$Apply) [1] pluginsPt(List[Any], Trees$Select) [1] pluginsPt(String @testAnn, Trees$Select) [1] pluginsPt(String, Trees$Apply) [1] pluginsPt(String, Trees$Block) [2] -pluginsPt(String, Trees$Ident) [4] +pluginsPt(String, Trees$Ident) [3] pluginsPt(String, Trees$Literal) [1] pluginsPt(String, Trees$Select) [1] -pluginsPt(String, Trees$Typed) [1] pluginsPt(Unit, Trees$Assign) [1] pluginsPt(testAnn, Trees$Apply) [5] pluginsTypeSig(, Trees$Template) [2] @@ -119,7 +114,7 @@ pluginsTyped(=> Int, Trees$TypeApply) [1] pluginsTyped(=> String @testAnn, Trees$Select) [1] pluginsTyped(A, Trees$Apply) [1] pluginsTyped(A, Trees$Ident) [2] -pluginsTyped(A, Trees$This) [8] +pluginsTyped(A, Trees$This) [1] pluginsTyped(A, Trees$TypeTree) [4] pluginsTyped(A.super.type, Trees$Super) [1] pluginsTyped(A.this.type, Trees$This) [11] @@ -128,25 +123,23 @@ pluginsTyped(AnyRef, Trees$Select) [4] pluginsTyped(Array[Any], Trees$ArrayValue) [1] pluginsTyped(Boolean @testAnn, Trees$Select) [1] pluginsTyped(Boolean @testAnn, Trees$TypeTree) [4] -pluginsTyped(Boolean(false), Trees$Literal) [2] +pluginsTyped(Boolean(false), Trees$Literal) [1] pluginsTyped(Boolean, Trees$Apply) [1] -pluginsTyped(Boolean, Trees$Select) [4] +pluginsTyped(Boolean, Trees$Select) [3] pluginsTyped(Char('c'), Trees$Literal) [2] pluginsTyped(Double, Trees$Apply) [3] pluginsTyped(Double, Trees$Select) [6] pluginsTyped(Int @testAnn, Trees$TypeTree) [2] pluginsTyped(Int @testAnn, Trees$Typed) [2] -pluginsTyped(Int(0), Trees$Literal) [3] +pluginsTyped(Int(0), Trees$Literal) [2] pluginsTyped(Int(1) @testAnn, Trees$Typed) [1] pluginsTyped(Int(1), Trees$Literal) [8] pluginsTyped(Int(2), Trees$Literal) [1] pluginsTyped(Int, Trees$Apply) [1] -pluginsTyped(Int, Trees$Ident) [2] -pluginsTyped(Int, Trees$If) [2] -pluginsTyped(Int, Trees$Select) [15] +pluginsTyped(Int, Trees$Ident) [1] +pluginsTyped(Int, Trees$If) [1] +pluginsTyped(Int, Trees$Select) [12] pluginsTyped(Int, Trees$TypeTree) [13] -pluginsTyped(List, Trees$Apply) [1] -pluginsTyped(List, Trees$Select) [1] pluginsTyped(List[Any], Trees$Apply) [1] pluginsTyped(List[Any], Trees$Select) [1] pluginsTyped(List[Any], Trees$TypeTree) [3] @@ -159,15 +152,13 @@ pluginsTyped(String(""), Trees$Literal) [2] pluginsTyped(String("huhu"), Trees$Literal) [1] pluginsTyped(String("str") @testAnn, Trees$Typed) [1] pluginsTyped(String("str"), Trees$Literal) [1] -pluginsTyped(String("str"), Trees$Typed) [1] pluginsTyped(String("two"), Trees$Literal) [2] pluginsTyped(String, Trees$Apply) [2] pluginsTyped(String, Trees$Block) [2] -pluginsTyped(String, Trees$Ident) [1] -pluginsTyped(String, Trees$Select) [9] +pluginsTyped(String, Trees$Select) [7] pluginsTyped(String, Trees$TypeTree) [7] pluginsTyped(Unit, Trees$Apply) [2] -pluginsTyped(Unit, Trees$Assign) [8] +pluginsTyped(Unit, Trees$Assign) [1] pluginsTyped(Unit, Trees$Block) [4] pluginsTyped(Unit, Trees$If) [1] pluginsTyped(Unit, Trees$Literal) [5] diff --git a/test/files/run/programmatic-main.check b/test/files/run/programmatic-main.check index 280a4f43d5..03f8273c17 100644 --- a/test/files/run/programmatic-main.check +++ b/test/files/run/programmatic-main.check @@ -10,17 +10,18 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - tailcalls 11 replace tail calls by jumps - specialize 12 @specialized-driven class and method specialization - explicitouter 13 this refs to outer pointers - erasure 14 erase types, add interfaces for traits - posterasure 15 clean up erased inline classes - lazyvals 16 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 17 move nested functions to top level - constructors 18 move field definitions into constructors - flatten 19 eliminate inner classes - mixin 20 mixin composition - cleanup 21 platform-specific cleanups, generate reflective calls - delambdafy 22 remove lambdas - jvm 23 generate JVM bytecode - terminal 24 the last phase during a compilation run + fields 11 synthesize accessors and fields + tailcalls 12 replace tail calls by jumps + specialize 13 @specialized-driven class and method specialization + explicitouter 14 this refs to outer pointers + erasure 15 erase types, add interfaces for traits + posterasure 16 clean up erased inline classes + lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs + lambdalift 18 move nested functions to top level + constructors 19 move field definitions into constructors + flatten 20 eliminate inner classes + mixin 21 mixin composition + cleanup 22 platform-specific cleanups, generate reflective calls + delambdafy 23 remove lambdas + jvm 24 generate JVM bytecode + terminal 25 the last phase during a compilation run diff --git a/test/files/run/reflection-fieldsymbol-navigation.check b/test/files/run/reflection-fieldsymbol-navigation.check index ae0597a045..fd06c78a18 100644 --- a/test/files/run/reflection-fieldsymbol-navigation.check +++ b/test/files/run/reflection-fieldsymbol-navigation.check @@ -1,6 +1,6 @@ -method x +variable x false variable x true -method x -method x_= +variable x +variable x diff --git a/test/files/run/repl-colon-type.check b/test/files/run/repl-colon-type.check index 21fbe34d96..1217e8d8c2 100644 --- a/test/files/run/repl-colon-type.check +++ b/test/files/run/repl-colon-type.check @@ -35,7 +35,7 @@ Int scala> :type protected lazy val f = 5 :5: error: lazy value f cannot be accessed in object $iw - Access to protected value f not permitted because + Access to protected lazy value f not permitted because enclosing object $eval in package $line13 is not a subclass of object $iw where target is defined lazy val $result = f diff --git a/test/files/run/showdecl.check b/test/files/run/showdecl.check index b8d7f94c57..d431c36f6d 100644 --- a/test/files/run/showdecl.check +++ b/test/files/run/showdecl.check @@ -8,7 +8,7 @@ initialized y: lazy val y: Int uninitialized z: def z: initialized z: def z: Int uninitialized t: def t: -initialized t: def t[T <: Int](x: D)(y: x.W): Int +initialized t: def t[T <: ](x: D)(y: x.W): Int uninitialized W: type W = String initialized W: type W = String uninitialized C: class C extends diff --git a/test/files/run/showdecl/Macros_1.scala b/test/files/run/showdecl/Macros_1.scala index c68dd275de..89b8e8d3c2 100644 --- a/test/files/run/showdecl/Macros_1.scala +++ b/test/files/run/showdecl/Macros_1.scala @@ -9,7 +9,7 @@ object Macros { import c.universe._ def test(sym: Symbol): Unit = { println(s"uninitialized ${sym.name}: ${showDecl(sym)}") - sym.info + sym.info // NOTE: not fullyInitializeSymbol, so some parts may still be LazyTypes println(s"initialized ${sym.name}: ${showDecl(sym)}") } diff --git a/test/files/run/showraw_mods.check b/test/files/run/showraw_mods.check index 4d34160422..ff77d22adf 100644 --- a/test/files/run/showraw_mods.check +++ b/test/files/run/showraw_mods.check @@ -1 +1 @@ -Block(List(ClassDef(Modifiers(ABSTRACT | DEFAULTPARAM/TRAIT), TypeName("C"), List(), Template(List(Ident(TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), TermName("$init$"), List(), List(List()), TypeTree(), Block(List(), Literal(Constant(())))), ValDef(Modifiers(PRIVATE | LOCAL), TermName("x"), TypeTree(), Literal(Constant(2))), ValDef(Modifiers(MUTABLE), TermName("y"), TypeTree(), Select(This(TypeName("C")), TermName("x"))), ValDef(Modifiers(LAZY), TermName("z"), TypeTree(), Select(This(TypeName("C")), TermName("y"))))))), Literal(Constant(()))) +Block(List(ClassDef(Modifiers(ABSTRACT | DEFAULTPARAM/TRAIT), TypeName("C"), List(), Template(List(Ident(TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), TermName("$init$"), List(), List(List()), TypeTree(), Block(List(), Literal(Constant(())))), DefDef(Modifiers(PRIVATE | METHOD | LOCAL | STABLE | ACCESSOR), TermName("x"), List(), List(), TypeTree(), Literal(Constant(2))), DefDef(Modifiers(METHOD | ACCESSOR), TermName("y"), List(), List(), TypeTree(), Select(This(TypeName("C")), TermName("x"))), DefDef(Modifiers(METHOD | ACCESSOR), TermName("y_$eq"), List(), List(List(ValDef(Modifiers(PARAM | SYNTHETIC), TermName("x$1"), TypeTree(), EmptyTree))), TypeTree(), EmptyTree), ValDef(Modifiers(LAZY), TermName("z"), TypeTree(), Select(This(TypeName("C")), TermName("y"))))))), Literal(Constant(()))) diff --git a/test/files/run/t4287inferredMethodTypes.check b/test/files/run/t4287inferredMethodTypes.check deleted file mode 100644 index 56e9c097cc..0000000000 --- a/test/files/run/t4287inferredMethodTypes.check +++ /dev/null @@ -1,30 +0,0 @@ -[[syntax trees at end of typer]] // newSource1.scala -[0:92]package [0:0] { - [0:21]class A extends [7:21][23]scala.AnyRef { - [8:16] private[this] val a: [8]Int = _; - <8:20>def (<8:20>a: [11] = [17:20]A.a): [7]A = <8:20>{ - <8:20><8:20><8:20>A.super.(); - <8:20>() - } - }; - [23:47]object A extends [32:47][49]scala.AnyRef { - [49]def (): [32]A.type = [49]{ - [49][49][49]A.super.(); - [32]() - }; - [36:45]private[this] val a: [40]Int = [44:45]2; - [40] def a: [40]Int = [40][40]A.this.a; - [8] def $default$1: [8]Int = [19]A.a - }; - [49:92]class B extends [57:92][65:66]A { - [65]def (): [57]B = [65]{ - [65][65][65]B.super.([65]A.$default$1); - [57]() - }; - [70:90]def ([79:80]a: [79]Int): [74]B = [84:90]{ - [84:90][84:90][84]B.this.(); - [84]() - } - } -} - diff --git a/test/files/run/t4287inferredMethodTypes.scala b/test/files/run/t4287inferredMethodTypes.scala deleted file mode 100644 index f14e672da8..0000000000 --- a/test/files/run/t4287inferredMethodTypes.scala +++ /dev/null @@ -1,25 +0,0 @@ -import scala.tools.partest.DirectTest - -object Test extends DirectTest { - - override def extraSettings: String = - s"-usejavacp -Yinfer-argument-types -Xprint-pos -Xprint:typer -Yrangepos -Ystop-after:typer -d ${testOutput.path}" - - override def code = """ -class A(a: Int = A.a) - -object A { - val a = 2 -} - -class B extends A { - def this(a) = this() -} - """.trim - - override def show(): Unit = { - Console.withErr(System.out) { - compile() - } - } -} \ No newline at end of file diff --git a/test/files/run/t6733.check b/test/files/run/t6733.check index aeb595fbfd..7062301c56 100644 --- a/test/files/run/t6733.check +++ b/test/files/run/t6733.check @@ -2,23 +2,22 @@ method $init$: isPrivateThis = false, isProtectedThis = false value pri1a: isPrivateThis = true, isProtectedThis = false method pri2a: isPrivateThis = true, isProtectedThis = false variable pri3a: isPrivateThis = true, isProtectedThis = false -value pri4a: isPrivateThis = true, isProtectedThis = false +variable pri3a: isPrivateThis = true, isProtectedThis = false +lazy value pri4a: isPrivateThis = true, isProtectedThis = false lazy value pri4a: isPrivateThis = true, isProtectedThis = false type Pri5a: isPrivateThis = true, isProtectedThis = false class Pri6: isPrivateThis = true, isProtectedThis = false trait Pri7: isPrivateThis = true, isProtectedThis = false object Pri8: isPrivateThis = true, isProtectedThis = false value pro1a: isPrivateThis = false, isProtectedThis = true -value pro1a: isPrivateThis = true, isProtectedThis = false value pro1b: isPrivateThis = false, isProtectedThis = true method pro2a: isPrivateThis = false, isProtectedThis = true method pro2b: isPrivateThis = false, isProtectedThis = true -method pro3a: isPrivateThis = false, isProtectedThis = true -method pro3a_=: isPrivateThis = false, isProtectedThis = true -variable pro3a: isPrivateThis = true, isProtectedThis = false -method pro3b: isPrivateThis = false, isProtectedThis = true -method pro3b_=: isPrivateThis = false, isProtectedThis = true -value pro4a: isPrivateThis = false, isProtectedThis = true +variable pro3a: isPrivateThis = false, isProtectedThis = true +variable pro3a: isPrivateThis = false, isProtectedThis = true +variable pro3b: isPrivateThis = false, isProtectedThis = true +variable pro3b: isPrivateThis = false, isProtectedThis = true +lazy value pro4a: isPrivateThis = false, isProtectedThis = true lazy value pro4a: isPrivateThis = true, isProtectedThis = false type Pro5a: isPrivateThis = false, isProtectedThis = true type Pro5b: isPrivateThis = false, isProtectedThis = true diff --git a/test/files/run/t7533.check b/test/files/run/t7533.check index fa5b3edc8f..61fd4657bd 100644 --- a/test/files/run/t7533.check +++ b/test/files/run/t7533.check @@ -1,30 +1,29 @@ Testing Symbol.isAbstract... =======class C======= -class C => true -constructor C => false -value x1 => true -value x2 => false -value x2 => false -method y1 => true -method y2 => false -type T1 => true -type T2 => false +class C => abstract +constructor C => concrete +value xAbs => abstract +value x => concrete +value x => concrete +method yAbs => abstract +method y => concrete +type TAbs => abstract +type T => concrete =======trait T======= -trait T => true -method $init$ => false -value z1 => true -value z2 => false -value z2 => false -method w1 => true -method w2 => false -type U1 => true -type U2 => false -=======class D======= -class D => false -constructor D => false -value x1 => false -value x1 => false -method y1 => false +trait T => abstract +method $init$ => concrete +value zAbs => abstract +value z => concrete +method wAbs => abstract +method w => concrete +type UAbs => abstract +type U => concrete +=======class AllConcrete======= +class AllConcrete => concrete +constructor AllConcrete => concrete +value xAbs => concrete +value xAbs => concrete +method yAbs => concrete =======object M======= -object M => false -constructor M => false +object M => concrete +constructor M => concrete diff --git a/test/files/run/t7533.scala b/test/files/run/t7533.scala index c7bd8e8d43..65c5c26b42 100644 --- a/test/files/run/t7533.scala +++ b/test/files/run/t7533.scala @@ -1,24 +1,24 @@ import scala.reflect.runtime.universe._ abstract class C { - val x1: Int - val x2: Int = 2 - def y1: Int - def y2: Int = 2 - type T1 <: Int - type T2 = Int + val xAbs: Int + val x: Int = 2 + def yAbs: Int + def y: Int = 2 + type TAbs <: Int + type T = Int } trait T { - val z1: Int - val z2: Int = 2 - def w1: Int - def w2: Int = 2 - type U1 <: Int - type U2 = Int + val zAbs: Int + val z: Int = 2 + def wAbs: Int + def w: Int = 2 + type UAbs <: Int + type U = Int } -class D extends C { - val x1 = 3 - def y1 = 3 +class AllConcrete extends C { + val xAbs = 3 + def yAbs = 3 } object M @@ -27,12 +27,12 @@ object Test extends App { def test[T: TypeTag] = { val sym = typeOf[T].typeSymbol println(s"=======$sym=======") - def printAbstract(sym: Symbol) = println(s"$sym => ${sym.isAbstract}") + def printAbstract(sym: Symbol) = println(s"$sym => ${if (sym.isAbstract) "abstract" else "concrete"}") printAbstract(sym) sym.info.decls.sorted.foreach(printAbstract) } test[C] test[T] - test[D] + test[AllConcrete] test[M.type] } \ No newline at end of file diff --git a/test/files/run/t8549.scala b/test/files/run/t8549.scala index da7a731459..7a38491231 100644 --- a/test/files/run/t8549.scala +++ b/test/files/run/t8549.scala @@ -79,7 +79,7 @@ object Test extends App { } } - // Generated on 20160715-08:27:53 with Scala version 2.12.0-20160715-012500-f5a80bd) + // Generated on 20160720-18:56:11 with Scala version 2.12.0-local-5815f9a) overwrite.foreach(updateComment) check(Some(1))("rO0ABXNyAApzY2FsYS5Tb21lESLyaV6hi3QCAAFMAAV2YWx1ZXQAEkxqYXZhL2xhbmcvT2JqZWN0O3hyAAxzY2FsYS5PcHRpb27+aTf92w5mdAIAAHhwc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAE=") @@ -174,7 +174,7 @@ object Test extends App { // check(mutable.ArraySeq(1, 2, 3))( "rO0ABXNyACFzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQXJyYXlTZXEVPD3SKEkOcwIAAkkABmxlbmd0aFsABWFycmF5dAATW0xqYXZhL2xhbmcvT2JqZWN0O3hwAAAAA3VyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAANzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4ABQAAAAJzcQB+AAUAAAAD") check(mutable.AnyRefMap("a" -> "A"))( "rO0ABXNyACJzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQW55UmVmTWFwAAAAAAAAAAECAAdJAAVfc2l6ZUkAB192YWNhbnRJAARtYXNrTAAMZGVmYXVsdEVudHJ5dAARTHNjYWxhL0Z1bmN0aW9uMTtbACtzY2FsYSRjb2xsZWN0aW9uJG11dGFibGUkQW55UmVmTWFwJCRfaGFzaGVzdAACW0lbAClzY2FsYSRjb2xsZWN0aW9uJG11dGFibGUkQW55UmVmTWFwJCRfa2V5c3QAE1tMamF2YS9sYW5nL09iamVjdDtbACtzY2FsYSRjb2xsZWN0aW9uJG11dGFibGUkQW55UmVmTWFwJCRfdmFsdWVzcQB+AAN4cAAAAAEAAAAAAAAAB3NyADNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQW55UmVmTWFwJEV4Y2VwdGlvbkRlZmF1bHQAAAAAAAAAAQIAAHhwdXIAAltJTbpgJnbqsqUCAAB4cAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+UkA2AAAAAHVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAhwcHBwcHB0AAFhcHVxAH4ACQAAAAhwcHBwcHB0AAFBcA==") check(mutable.ArrayStack(1, 2, 3))( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQXJyYXlTdGFja3bdxXbcnLBeAgACSQAqc2NhbGEkY29sbGVjdGlvbiRtdXRhYmxlJEFycmF5U3RhY2skJGluZGV4WwAqc2NhbGEkY29sbGVjdGlvbiRtdXRhYmxlJEFycmF5U3RhY2skJHRhYmxldAATW0xqYXZhL2xhbmcvT2JqZWN0O3hwAAAAA3VyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAANzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAA3NxAH4ABQAAAAJzcQB+AAUAAAAB") - check(mutable.DoubleLinkedList(1, 2, 3))( "rO0ABXNyAClzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuRG91YmxlTGlua2VkTGlzdI73LKsKRr1RAgADTAAEZWxlbXQAEkxqYXZhL2xhbmcvT2JqZWN0O0wABG5leHR0AB5Mc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL1NlcTtMAARwcmV2cQB+AAJ4cHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3EAfgAAc3EAfgAEAAAAAnNxAH4AAHNxAH4ABAAAAANzcQB+AABwcQB+AAtxAH4ACXEAfgAHcQB+AANw") + check(mutable.DoubleLinkedList(1, 2, 3))( "rO0ABXNyAClzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuRG91YmxlTGlua2VkTGlzdI73LKsKRr1RAgADTAAEZWxlbXQAEkxqYXZhL2xhbmcvT2JqZWN0O0wABG5leHR0ACtMc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL0RvdWJsZUxpbmtlZExpc3Q7TAAEcHJldnEAfgACeHBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4AAHNxAH4ABAAAAAJzcQB+AABzcQB+AAQAAAADc3EAfgAAcHEAfgALcQB+AAlxAH4AB3EAfgADcA==") check(mutable.HashMap())( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuSGFzaE1hcAAAAAAAAAABAwAAeHB3DQAAAu4AAAAAAAAABAB4") check(mutable.HashMap(1 -> 1))( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuSGFzaE1hcAAAAAAAAAABAwAAeHB3DQAAAu4AAAABAAAABABzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXEAfgAEeA==") @@ -189,7 +189,7 @@ object Test extends App { // check(new mutable.History())( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuSGlzdG9yeUhuXxDIFJrsAgACSQAKbWF4SGlzdG9yeUwAA2xvZ3QAIExzY2FsYS9jb2xsZWN0aW9uL211dGFibGUvUXVldWU7eHAAAAPoc3IAHnNjYWxhLmNvbGxlY3Rpb24ubXV0YWJsZS5RdWV1ZbjMURVfOuHHAgAAeHIAJHNjYWxhLmNvbGxlY3Rpb24ubXV0YWJsZS5NdXRhYmxlTGlzdFJpnjJ+gFbAAgADSQADbGVuTAAGZmlyc3QwdAAlTHNjYWxhL2NvbGxlY3Rpb24vbXV0YWJsZS9MaW5rZWRMaXN0O0wABWxhc3QwcQB+AAV4cAAAAABzcgAjc2NhbGEuY29sbGVjdGlvbi5tdXRhYmxlLkxpbmtlZExpc3Sak+nGCZHaUQIAAkwABGVsZW10ABJMamF2YS9sYW5nL09iamVjdDtMAARuZXh0dAAeTHNjYWxhL2NvbGxlY3Rpb24vbXV0YWJsZS9TZXE7eHBwcQB+AApxAH4ACg==") check(mutable.LinkedHashMap(1 -> 2))( "rO0ABXNyACZzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlua2VkSGFzaE1hcAAAAAAAAAABAwAAeHB3DQAAAu4AAAABAAAABABzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4AAgAAAAJ4") check(mutable.LinkedHashSet(1, 2, 3))( "rO0ABXNyACZzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlua2VkSGFzaFNldAAAAAAAAAABAwAAeHB3DQAAAu4AAAADAAAABABzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4AAgAAAAJzcQB+AAIAAAADeA==") - check(mutable.LinkedList(1, 2, 3))( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlua2VkTGlzdJqT6cYJkdpRAgACTAAEZWxlbXQAEkxqYXZhL2xhbmcvT2JqZWN0O0wABG5leHR0AB5Mc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL1NlcTt4cHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3EAfgAAc3EAfgAEAAAAAnNxAH4AAHNxAH4ABAAAAANzcQB+AABwcQB+AAs=") + check(mutable.LinkedList(1, 2, 3))( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlua2VkTGlzdJqT6cYJkdpRAgACTAAEZWxlbXQAEkxqYXZhL2xhbmcvT2JqZWN0O0wABG5leHR0ACVMc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL0xpbmtlZExpc3Q7eHBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4AAHNxAH4ABAAAAAJzcQB+AABzcQB+AAQAAAADc3EAfgAAcHEAfgAL") // TODO SI-8576 unstable under -Xcheckinit // check(mutable.ListBuffer(1, 2, 3))( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlzdEJ1ZmZlci9y9I7QyWzGAwAEWgAIZXhwb3J0ZWRJAANsZW5MAAVsYXN0MHQAKUxzY2FsYS9jb2xsZWN0aW9uL2ltbXV0YWJsZS8kY29sb24kY29sb247TAAqc2NhbGEkY29sbGVjdGlvbiRtdXRhYmxlJExpc3RCdWZmZXIkJHN0YXJ0dAAhTHNjYWxhL2NvbGxlY3Rpb24vaW1tdXRhYmxlL0xpc3Q7eHBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4ABAAAAAJzcQB+AAQAAAADc3IALHNjYWxhLmNvbGxlY3Rpb24uaW1tdXRhYmxlLkxpc3RTZXJpYWxpemVFbmQkilxjW/dTC20CAAB4cHcFAAAAAAN4") diff --git a/test/files/run/trait_fields_bytecode.scala b/test/files/run/trait_fields_bytecode.scala new file mode 100644 index 0000000000..d87412f43e --- /dev/null +++ b/test/files/run/trait_fields_bytecode.scala @@ -0,0 +1,23 @@ +trait TFinal { final val bla: Int = 123 } + +// bla should be final in C +class CFinal extends TFinal + + +trait TConst { final val C = "S" } +// there should be a C method in `T$class`! +class CConst extends TConst { } + + +object Test { + def main(args: Array[String]): Unit = { + val f1 = classOf[CFinal].getDeclaredMethod("bla") + import java.lang.reflect.Modifier._ + assert(isFinal(f1.getModifiers), f1) + + classOf[CConst].getMethod("C") + + import language.reflectiveCalls + assert(new CConst().asInstanceOf[{def C: String}].C == "S") + } +} diff --git a/test/files/run/trait_fields_final.scala b/test/files/run/trait_fields_final.scala new file mode 100644 index 0000000000..8b32e5b47d --- /dev/null +++ b/test/files/run/trait_fields_final.scala @@ -0,0 +1,21 @@ +// TODO: clarify meaning of final in traits +// In the new compiler, there's no final modifier after mixin for `meh`'s setter, +// whereas 2.12.0-M3 makes meh's trait setter final. +// NOTE: bytecode is identical, but the scalasignature is different +trait Foo { self: Meh => + def bar(x: String) = x == "a" + private final val meh = bar("a") +} + +abstract class Meh extends Foo + +object Test { + def main(args: Array[String]): Unit = { + val setter = classOf[Meh].getDeclaredMethod("Foo$_setter_$Foo$$meh_$eq", java.lang.Boolean.TYPE) + val getter = classOf[Meh].getDeclaredMethod("Foo$$meh") + import java.lang.reflect.Modifier._ + assert(isFinal(setter.getModifiers), setter) + assert(isFinal(getter.getModifiers), getter) + } + +} diff --git a/test/files/run/trait_fields_init.check b/test/files/run/trait_fields_init.check new file mode 100644 index 0000000000..84c1a2ead9 --- /dev/null +++ b/test/files/run/trait_fields_init.check @@ -0,0 +1,21 @@ +x +y +z +abstract +public +protected +abstract protected +private +private[this] +abstract +public +protected +abstract protected +private +private[this] +abstract +public +protected +abstract protected +private +private[this] diff --git a/test/files/run/trait_fields_init.scala b/test/files/run/trait_fields_init.scala new file mode 100644 index 0000000000..496911d538 --- /dev/null +++ b/test/files/run/trait_fields_init.scala @@ -0,0 +1,55 @@ +trait T { + val abs: String + protected val protabs: String + val pub = "public" + protected val prot = "protected" + private val privvy = "private" + private[this] val privateThis = "private[this]" + // TODO: + // final val const = "const" + + trait Nested { println(abs + privateThis) } + + object NO { + println(abs) + println(pub) + println(prot) + println(protabs) + println(privvy) + println(privateThis) + } + + trait NT { + println(abs) + println(pub) + println(prot) + println(protabs) + println(privvy) + println(privateThis) + } + + class NC { + println(abs) + println(pub) + println(prot) + println(protabs) + println(privvy) + println(privateThis) + } +} + +class C extends AnyRef with T { + println("x") + val abs = "abstract" + println("y") + val protabs = "abstract protected" + final val const = "const" + println("z") +} + +object Test extends C { + def main(args: Array[String]): Unit = { + NO + new NT{} + new NC +}} \ No newline at end of file diff --git a/test/files/run/trait_fields_repl.check b/test/files/run/trait_fields_repl.check new file mode 100644 index 0000000000..d03a565c7b --- /dev/null +++ b/test/files/run/trait_fields_repl.check @@ -0,0 +1,11 @@ + +scala> trait B { val y = "a" } +defined trait B + +scala> trait T extends B { val x: y.type = y } +defined trait T + +scala> println((new T{}).x) +a + +scala> :quit diff --git a/test/files/run/trait_fields_repl.scala b/test/files/run/trait_fields_repl.scala new file mode 100644 index 0000000000..311477b7d2 --- /dev/null +++ b/test/files/run/trait_fields_repl.scala @@ -0,0 +1,10 @@ +// TODO: fix AME when this runs in REPL +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def code = """ +trait B { val y = "a" } +trait T extends B { val x: y.type = y } +println((new T{}).x) +""" +} diff --git a/test/files/run/trait_fields_three_layer_overrides.check b/test/files/run/trait_fields_three_layer_overrides.check new file mode 100644 index 0000000000..8bb45803c5 --- /dev/null +++ b/test/files/run/trait_fields_three_layer_overrides.check @@ -0,0 +1,2 @@ +the real universe.TypeTag +1 diff --git a/test/files/run/trait_fields_three_layer_overrides.scala b/test/files/run/trait_fields_three_layer_overrides.scala new file mode 100644 index 0000000000..9d7aa94341 --- /dev/null +++ b/test/files/run/trait_fields_three_layer_overrides.scala @@ -0,0 +1,25 @@ +// interesting hierarchies/overrides distilled from reflect/compiler + +trait Aliases { + val TypeTag = "universe.TypeTag" +} +trait AliasesOverrides extends Aliases { // or self: Aliases => + override val TypeTag = "the real universe.TypeTag" +} +class Context extends Aliases with AliasesOverrides + + + +trait SymbolTable { + def currentRunId: Int = -1 +} +trait ReflectSetup extends SymbolTable { + override val currentRunId = 1 +} +class G extends SymbolTable with ReflectSetup + + +object Test extends App { + println((new Context).TypeTag) + println((new G).currentRunId) +} \ No newline at end of file diff --git a/test/files/run/trait_fields_volatile.scala b/test/files/run/trait_fields_volatile.scala new file mode 100644 index 0000000000..eedb6de1c2 --- /dev/null +++ b/test/files/run/trait_fields_volatile.scala @@ -0,0 +1,13 @@ +// bytecode should reflect volatile annotation +trait VolatileAbort { + @volatile private var abortflag = false +} +class DefaultSignalling extends VolatileAbort + +object Test { + def main(args: Array[String]): Unit = { + val field = classOf[DefaultSignalling].getDeclaredFields.find(_.getName.contains("abortflag")).get + assert(java.lang.reflect.Modifier.isVolatile(field.getModifiers), field) + } + +} -- cgit v1.2.3 From 8fa63b7538e169a4b72b95d9dd8fa7a8939279d9 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 30 May 2016 20:30:16 +0200 Subject: Test EnclosingMethod attribute for classes in lazy vals Local and anonymous classes need to have an EnclosingMethod attribute denoting the enclosing class and method. In fact, the enclosing class must always be defined for local and anonymous classes, but the enclosing method may be null (for local / anonymous classes defined in field initializers or local blocks within a class body). The new test here ensures that classes declared within a lazy val initializer block indeed have the enclosing method set to null. --- test/files/jvm/innerClassAttribute/Classes_1.scala | 37 ++++++++++++++++++++++ test/files/jvm/innerClassAttribute/Test.scala | 17 +++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) (limited to 'test/files') diff --git a/test/files/jvm/innerClassAttribute/Classes_1.scala b/test/files/jvm/innerClassAttribute/Classes_1.scala index bffc495b4f..27f01a880a 100644 --- a/test/files/jvm/innerClassAttribute/Classes_1.scala +++ b/test/files/jvm/innerClassAttribute/Classes_1.scala @@ -303,3 +303,40 @@ object NestedInValueClass { def f = { class C; new C } // outer class A$, outer method f } } + +object LocalAndAnonymousInLazyInitializer { + abstract class A + class C { + lazy val a: A = new A { } + lazy val b: A = { + class AA extends A + new AA + } + lazy val c: A = { + object AA extends A + AA + } + } + object O { + lazy val a: A = new A { } + lazy val b: A = { + class AA extends A + new AA + } + lazy val c: A = { + object AA extends A + AA + } + } + trait T { + lazy val a: A = new A { } + lazy val b: A = { + class AA extends A + new AA + } + lazy val c: A = { + object AA extends A + AA + } + } +} \ No newline at end of file diff --git a/test/files/jvm/innerClassAttribute/Test.scala b/test/files/jvm/innerClassAttribute/Test.scala index 1b78773d42..5c666a615f 100644 --- a/test/files/jvm/innerClassAttribute/Test.scala +++ b/test/files/jvm/innerClassAttribute/Test.scala @@ -416,7 +416,7 @@ object Test extends BytecodeTest { def testAnonymousClassesMayBeNestedInSpecialized() { assertEnclosingMethod("AnonymousClassesMayBeNestedInSpecialized$C$$anon$17", "AnonymousClassesMayBeNestedInSpecialized$C", "foo", "(Ljava/lang/Object;)LAnonymousClassesMayBeNestedInSpecialized$A;") - assertEnclosingMethod("AnonymousClassesMayBeNestedInSpecialized$C$mcI$sp$$anon$18", "AnonymousClassesMayBeNestedInSpecialized$C$mcI$sp", "foo$mcI$sp", "(I)LAnonymousClassesMayBeNestedInSpecialized$A;") + assertEnclosingMethod("AnonymousClassesMayBeNestedInSpecialized$C$mcI$sp$$anon$21", "AnonymousClassesMayBeNestedInSpecialized$C$mcI$sp", "foo$mcI$sp", "(I)LAnonymousClassesMayBeNestedInSpecialized$A;") } def testNestedInValueClass() { @@ -444,6 +444,20 @@ object Test extends BytecodeTest { testInner("NestedInValueClass$A$", a, am, b, c, methodHandlesLookup) } + def testLocalAndAnonymousInLazyInitializer(): Unit = { + assertEnclosingMethod("LocalAndAnonymousInLazyInitializer$C$$anon$18", "LocalAndAnonymousInLazyInitializer$C", null, null) + assertEnclosingMethod("LocalAndAnonymousInLazyInitializer$C$AA$4", "LocalAndAnonymousInLazyInitializer$C", null, null) + assertEnclosingMethod("LocalAndAnonymousInLazyInitializer$C$AA$5$", "LocalAndAnonymousInLazyInitializer$C", null, null) + + assertEnclosingMethod("LocalAndAnonymousInLazyInitializer$O$$anon$19", "LocalAndAnonymousInLazyInitializer$O$", null, null) + assertEnclosingMethod("LocalAndAnonymousInLazyInitializer$O$AA$6", "LocalAndAnonymousInLazyInitializer$O$", null, null) + assertEnclosingMethod("LocalAndAnonymousInLazyInitializer$O$AA$7$", "LocalAndAnonymousInLazyInitializer$O$", null, null) + + assertEnclosingMethod("LocalAndAnonymousInLazyInitializer$T$$anon$20", "LocalAndAnonymousInLazyInitializer$T", null, null) + assertEnclosingMethod("LocalAndAnonymousInLazyInitializer$T$AA$8", "LocalAndAnonymousInLazyInitializer$T", null, null) + assertEnclosingMethod("LocalAndAnonymousInLazyInitializer$T$AA$9$", "LocalAndAnonymousInLazyInitializer$T", null, null) + } + def show(): Unit = { testA1() testA2() @@ -473,5 +487,6 @@ object Test extends BytecodeTest { testSpecializedClassesTopLevel() testAnonymousClassesMayBeNestedInSpecialized() testNestedInValueClass() + testLocalAndAnonymousInLazyInitializer() } } -- cgit v1.2.3 From fcfe7050a50d2c71094a9ac212330be87c4d0781 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 26 May 2016 20:09:28 -0700 Subject: Fields phase synthesizes modules For now, keep the info transform in refchecks. Ultimately, refchecks should only check, not transform trees/infos. Fixes https://github.com/scala/scala-dev/issues/126: the accessor for a module in a trait is correctly marked non-final (it's deferred). --- .../scala/tools/nsc/transform/Fields.scala | 308 ++++++++++++++------- src/compiler/scala/tools/nsc/transform/Mixin.scala | 2 +- .../scala/tools/nsc/typechecker/RefChecks.scala | 78 +----- src/reflect/scala/reflect/internal/Symbols.scala | 13 +- src/reflect/scala/reflect/internal/Variances.scala | 4 +- test/files/neg/t0764.check | 2 +- .../nsc/backend/jvm/opt/ScalaInlineInfoTest.scala | 2 +- 7 files changed, 221 insertions(+), 188 deletions(-) (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala index 0dd7b1fee0..f5f0b229e4 100644 --- a/src/compiler/scala/tools/nsc/transform/Fields.scala +++ b/src/compiler/scala/tools/nsc/transform/Fields.scala @@ -14,18 +14,24 @@ import symtab.Flags._ * * For traits: * - * - Namers translates a definition `val x = rhs` into a getter `def x = rhs` -- no underlying field is created. - * - This phase synthesizes accessors and fields for any vals mixed into a non-trait class. - * - Constructors will move the rhs to an assignment in the template body. - * and those statements then move to the template into the constructor, - * which means it will initialize the fields defined in this template (and execute the corresponding side effects). - * We need to maintain the connection between getter and rhs until after specialization so that it can duplicate vals. + * - Namers translates a definition `val x = rhs` into a getter `def x = rhs` -- no underlying field is created. + * - This phase synthesizes accessors and fields for any vals mixed into a non-trait class. + * - Constructors will move the rhs to an assignment in the template body. + * Those statements then move to the template into the constructor, + * which means it will initialize the fields defined in this template (and execute the corresponding side effects). + * We need to maintain the connection between getter and rhs until after specialization so that it can duplicate vals. + * - A ModuleDef is desugared to a ClassDef, an accessor (which reuses the module's term symbol) + * and a module var (unless the module is static and does not implement a member of a supertype, or we're in a trait). + * For subclasses of traits that define modules, a module var is mixed in, as well as the required module accessors. * + * Runs after uncurry to deal with classes that implement SAM traits with ValDefs. * Runs before erasure (to get bridges), and thus before lambdalift/flatten, so that nested functions/definitions must be considered. + * * We run after uncurry because it can introduce subclasses of traits with fields (SAMs with vals). * Lambdalift also introduces new fields (paramaccessors for captured vals), but runs too late in the pipeline * (mixins still synthesizes implementations for accessors that need to be mixed into subclasses of local traits that capture). * + * * In the future, would like to get closer to dotty, which lifts a val's RHS (a similar thing is done for template-level statements) * to a method `$_initialize_$1$x` instead of a block, which is used in the constructor to initialize the val. * This makes for a nice unification of strict and lazy vals, in that the RHS is lifted to a method for both, @@ -35,8 +41,8 @@ import symtab.Flags._ * if we encode the name (and place in initialisation order) of the field * in the name of its initializing method, to allow separate compilation. * (The name mangling must include ordering, and thus complicate incremental compilation: - * ideally, we'd avoid renumbering unchanged methods, but that would result in - * different bytecode between clean recompiles and incremental ones). + * ideally, we'd avoid renumbering unchanged methods, but that would result in + * different bytecode between clean recompiles and incremental ones). * * In the even longer term (Scala 3?), I agree with @DarkDimius that it would make sense * to hide the difference between strict and lazy vals. All vals are lazy, @@ -68,12 +74,14 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // TODO: reuse MIXEDIN for NEEDS_TREES? override def phaseNewFlags: Long = NEEDS_TREES | OVERRIDDEN_TRAIT_SETTER + // informs the tree traversal of the shape of the tree to emit + // (it's an *overridden* trait setter) private final val OVERRIDDEN_TRAIT_SETTER = TRANS_FLAG final val TRAIT_SETTER_FLAGS = NEEDS_TREES | DEFERRED | ProtectedLocal private def accessorImplementedInSubclass(accessor: Symbol) = - (accessor hasFlag SYNTHESIZE_IMPL_IN_SUBCLASS) && (accessor hasFlag (ACCESSOR)) + (accessor hasFlag SYNTHESIZE_IMPL_IN_SUBCLASS) && (accessor hasFlag (ACCESSOR | MODULE)) private def concreteOrSynthImpl(sym: Symbol): Boolean = !(sym hasFlag DEFERRED) || (sym hasFlag SYNTHESIZE_IMPL_IN_SUBCLASS) @@ -98,7 +106,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor ) - def checkAndClearOverridden(setter: Symbol) = checkAndClear(OVERRIDDEN_TRAIT_SETTER)(setter) + def checkAndClearOverriddenTraitSetter(setter: Symbol) = checkAndClear(OVERRIDDEN_TRAIT_SETTER)(setter) def checkAndClearNeedsTrees(setter: Symbol) = checkAndClear(NEEDS_TREES)(setter) def checkAndClear(flag: Long)(sym: Symbol) = sym.hasFlag(flag) match { @@ -162,6 +170,25 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor sym setAnnotations (sym.annotations filter AnnotationInfo.mkFilter(GetterTargetClass, defaultRetention = false)) } + + // can't use the referenced field since it already tracks the module's moduleClass + private[this] val moduleVarOf = perRunCaches.newMap[Symbol, Symbol] + + private def newModuleVarSymbol(site: Symbol, module: Symbol, tp: Type, extraFlags: Long): TermSymbol = { +// println(s"new module var in $site for $module of type $tp") + val moduleVar = site.newVariable(nme.moduleVarName(module.name.toTermName), module.pos.focus, MODULEVAR | extraFlags) setInfo tp addAnnotation VolatileAttr + moduleVarOf(module) = moduleVar + + moduleVar + } + + private def moduleInit(module: Symbol) = { +// println(s"moduleInit for $module in ${module.ownerChain} --> ${moduleVarOf.get(module)}") + val moduleVar = moduleVarOf(module) + gen.mkAssignAndReturn(moduleVar, gen.newModule(module, moduleVar.info)) + } + + private object synthFieldsAndAccessors extends TypeMap { private def newTraitSetter(getter: Symbol, clazz: Symbol) = { // Add setter for an immutable, memoizing getter @@ -178,6 +205,32 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor setter } + private def newModuleAccessor(module: Symbol, site: Symbol, moduleVar: Symbol) = { + val accessor = site.newMethod(module.name.toTermName, site.pos, STABLE | MODULE | NEEDS_TREES) + + moduleVarOf(accessor) = moduleVar + + // we're in the same prefix as module, so no need for site.thisType.memberType(module) + accessor setInfo MethodType(Nil, moduleVar.info) + accessor.setModuleClass(module.moduleClass) + + if (module.isPrivate) accessor.expandName(module.owner) + + accessor + } + + + // needed for the following scenario (T could be trait or class) + // trait T { def f: Object }; object O extends T { object f }. Need to generate method f in O. + // marking it as an ACCESSOR so that it will get to `getterBody` when synthesizing trees below + // it should not be considered a MODULE + def newMatchingModuleAccessor(clazz: Symbol, module: Symbol): MethodSymbol = { + val acc = clazz.newMethod(module.name.toTermName, module.pos, (module.flags & ~MODULE) | STABLE | NEEDS_TREES | ACCESSOR) + acc.referenced = module + acc setInfo MethodType(Nil, module.moduleClass.tpe) + } + + def apply(tp0: Type): Type = tp0 match { // TODO: make less destructive (name changes, decl additions, flag setting -- // none of this is actually undone when travelling back in time using atPhase) @@ -214,6 +267,11 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor if (member hasFlag STABLE) // TODO: check isGetter? newDecls += newTraitSetter(member, clazz) } + } else if (member hasFlag MODULE) { + nonStaticModuleToMethod(member) + + member setFlag NEEDS_TREES + synthesizeImplInSubclasses(member) } } @@ -228,27 +286,48 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor case tp@ClassInfoType(parents, oldDecls, clazz) if !clazz.isPackageClass => val site = clazz.thisType - // TODO (1): improve logic below, which is used to avoid mixing in anything that would result in an error in refchecks - // (a reason to run after refchecks? we should run before pickler, though, I think, so that the synthesized stats are pickled) - - val membersNeedingSynthesis = clazz.mixinClasses.flatMap { mixin => - // afterOwnPhase, so traits receive trait setters for vals - afterOwnPhase {mixin.info}.decls.toList.filter(accessorImplementedInSubclass) - } - -// println(s"mixing in for $clazz: $membersNeedingSynthesis from ${clazz.mixinClasses}") // TODO: setter conflicts? def accessorConflictsExistingVal(accessor: Symbol): Boolean = { val existingGetter = oldDecls.lookup(accessor.name.getterName) - // println(s"$existingGetter from $accessor to ${accessor.name.getterName}") +// println(s"$existingGetter from $accessor to ${accessor.name.getterName}") val tp = fieldTypeOfAccessorIn(accessor, site) (existingGetter ne NoSymbol) && (tp matches (site memberInfo existingGetter).resultType) // !existingGetter.isDeferred && -- see (3) } + def newModuleVar(member: Symbol): TermSymbol = + newModuleVarSymbol(clazz, member, site.memberType(member).resultType, PrivateLocal | SYNTHETIC | NEEDS_TREES) + + // a module does not need treatment here if it's static, unless it has a matching member in a superclass + // a non-static method needs a module var + val modulesNeedingExpansion = + oldDecls.toList.filter(m => m.isModule && (!m.isStatic || m.isOverridingSymbol)) + + // expand module def in class/object (if they need it -- see modulesNeedingExpansion above) + val expandedModules = + modulesNeedingExpansion map { module => + // expanding module def (top-level or nested in static module) + if (module.isStatic) { // implies m.isOverridingSymbol as per above filter + // Need a module accessor, to implement/override a matching member in a superclass. + // Never a need for a module var if the module is static. + newMatchingModuleAccessor(clazz, module) + } else { + nonStaticModuleToMethod(module) + // must reuse symbol instead of creating an accessor + module setFlag NEEDS_TREES + newModuleVar(module) + } + } + +// println(s"expanded modules for $clazz: $expandedModules") + + // afterOwnPhase, so traits receive trait setters for vals (needs to be at finest grain to avoid looping) + val synthInSubclass = + clazz.mixinClasses.flatMap(mixin => afterOwnPhase{mixin.info}.decls.toList.filter(accessorImplementedInSubclass)) + // mixin field accessors -- // invariant: (accessorsMaybeNeedingImpl, mixedInAccessorAndFields).zipped.forall(case (acc, clone :: _) => `clone` is clone of `acc` case _ => true) - val synthAccessorAndFields = membersNeedingSynthesis map { member => + val mixedInAccessorAndFields = synthInSubclass.map{ member => def cloneAccessor() = { val clonedAccessor = (member cloneSymbol clazz) setPos clazz.pos setMixedinAccessorFlags(member, clonedAccessor) @@ -258,13 +337,17 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // if we don't cloneInfo, method argument symbols are shared between trait and subclasses --> lambalift proxy crash // TODO: use derive symbol variant? - // println(s"cloning accessor $accessor to $clazz / $clonedInfo -> $relativeInfo") +// println(s"cloning accessor $member to $clazz") clonedAccessor setInfo ((clazz.thisType memberType member) cloneInfo clonedAccessor) // accessor.info.cloneInfo(clonedAccessor).asSeenFrom(clazz.thisType, accessor.owner) } + if (member hasFlag MODULE) { + val moduleVar = newModuleVar(member) + List(moduleVar, newModuleAccessor(member, clazz, moduleVar)) + } // when considering whether to mix in the trait setter, forget about conflicts -- they will be reported for the getter // a trait setter for an overridden val will receive a unit body in the tree transform - if (nme.isTraitSetterName(member.name)) { + else if (nme.isTraitSetterName(member.name)) { val getter = member.getterIn(member.owner) val clone = cloneAccessor() @@ -290,13 +373,13 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor } else List(cloneAccessor()) } - // println(s"new decls for $clazz: $mixedInAccessorAndFields") +// println(s"mixedInAccessorAndFields for $clazz: $mixedInAccessorAndFields") // omit fields that are not memoized, retain all other members def omittableField(sym: Symbol) = sym.isValue && !sym.isMethod && !fieldMemoizationIn(sym, clazz).stored val newDecls = - if (synthAccessorAndFields.isEmpty) oldDecls.filterNot(omittableField) + if (expandedModules.isEmpty && mixedInAccessorAndFields.isEmpty) oldDecls.filterNot(omittableField) else { // must not alter `decls` directly val newDecls = newScope @@ -304,12 +387,13 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor val enterAll = (_: List[Symbol]) foreach enter oldDecls foreach { d => if (!omittableField(d)) enter(d) } - synthAccessorAndFields foreach enterAll + expandedModules foreach enter + mixedInAccessorAndFields foreach enterAll newDecls } - // println(s"new decls: $newDecls") +// println(s"new decls for $clazz: $expandedModules ++ $mixedInAccessorAndFields") if (newDecls eq oldDecls) tp else ClassInfoType(parents, newDecls, clazz) @@ -319,6 +403,11 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor } + // done by uncurry's info transformer + // instead of forcing every member's info to run said transformer, duplicate the flag update logic... + def nonStaticModuleToMethod(module: Symbol): Unit = { + if (!module.isStatic) module setFlag METHOD | STABLE + } class FieldsTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { def mkTypedUnit(pos: Position) = localTyper.typedPos(pos)(CODE.UNIT) @@ -330,57 +419,64 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // synth trees for accessors/fields and trait setters when they are mixed into a class - def fieldsAndAccessors(exprOwner: Symbol): List[ValOrDefDef] = { - if (exprOwner.isLocalDummy) { - val clazz = exprOwner.owner - def fieldAccess(accessor: Symbol): Option[Tree] = { - val fieldName = accessor.localName - val field = clazz.info.decl(fieldName) - // The `None` result denotes an error, but we defer to refchecks to report it. - // This is the result of overriding a val with a def, so that no field is found in the subclass. - if (field.exists) Some(Select(This(clazz), field)) - else None - } + def fieldsAndAccessors(clazz: Symbol): List[ValOrDefDef] = { + def fieldAccess(accessor: Symbol): Option[Tree] = { + val fieldName = accessor.localName + val field = clazz.info.decl(fieldName) + // The `None` result denotes an error, but we defer to refchecks to report it. + // This is the result of overriding a val with a def, so that no field is found in the subclass. + if (field.exists) Some(Select(This(clazz), field)) + else None + } - def getterBody(getter: Symbol): Option[Tree] = { + def getterBody(getter: Symbol): Option[Tree] = { + // accessor created by newMatchingModuleAccessor for a static module that does need an accessor + // (because there's a matching member in a super class) + if (getter.asTerm.referenced.isModule) { + Some(gen.mkAttributedRef(clazz.thisType, getter.asTerm.referenced)) + } else { val fieldMemoization = fieldMemoizationIn(getter, clazz) if (fieldMemoization.pureConstant) Some(gen.mkAttributedQualifier(fieldMemoization.tp)) // TODO: drop when we no longer care about producing identical bytecode else fieldAccess(getter) } + } - // println(s"accessorsAndFieldsNeedingTrees for $templateSym: $accessorsAndFieldsNeedingTrees") - def setterBody(setter: Symbol): Option[Tree] = { - // trait setter in trait - if (clazz.isTrait) Some(EmptyTree) - // trait setter for overridden val in class - else if (checkAndClearOverridden(setter)) Some(mkTypedUnit(setter.pos)) - // trait val/var setter mixed into class - else fieldAccess(setter) map (fieldSel => Assign(fieldSel, Ident(setter.firstParam))) - } + // println(s"accessorsAndFieldsNeedingTrees for $templateSym: $accessorsAndFieldsNeedingTrees") + def setterBody(setter: Symbol): Option[Tree] = { + // trait setter in trait + if (clazz.isTrait) Some(EmptyTree) + // trait setter for overridden val in class + else if (checkAndClearOverriddenTraitSetter(setter)) Some(mkTypedUnit(setter.pos)) + // trait val/var setter mixed into class + else fieldAccess(setter) map (fieldSel => Assign(fieldSel, Ident(setter.firstParam))) + } + def moduleAccessorBody(module: Symbol): Some[Tree] = Some( + // added during synthFieldsAndAccessors using newModuleAccessor + // a module defined in a trait by definition can't be static (it's a member of the trait and thus gets a new instance for every outer instance) + if (clazz.isTrait) EmptyTree + // symbol created by newModuleAccessor for a (non-trait) class + else moduleInit(module) + ) - clazz.info.decls.toList.filter(checkAndClearNeedsTrees) flatMap { - case setter if setter.isSetter => setterBody(setter) map mkAccessor(setter) - case getter if getter.isAccessor => getterBody(getter) map mkAccessor(getter) - case field if !(field hasFlag METHOD) => Some(mkField(field)) // vals/vars and module vars (cannot have flags PACKAGE | JAVA since those never receive NEEDS_TREES) - case _ => None - } - } else { -// println(s"$exprOwner : ${exprOwner.info} --> ${exprOwner.info.decls}") - Nil + clazz.info.decls.toList.filter(checkAndClearNeedsTrees) flatMap { + case module if module hasAllFlags (MODULE | METHOD) => moduleAccessorBody(module) map mkAccessor(module) + case setter if setter.isSetter => setterBody(setter) map mkAccessor(setter) + case getter if getter.hasFlag(ACCESSOR) => getterBody(getter) map mkAccessor(getter) + case field if !(field hasFlag METHOD) => Some(mkField(field)) // vals/vars and module vars (cannot have flags PACKAGE | JAVA since those never receive NEEDS_TREES) + case _ => None } } def rhsAtOwner(stat: ValOrDefDef, newOwner: Symbol): Tree = atOwner(newOwner)(super.transform(stat.rhs.changeOwner(stat.symbol -> newOwner))) - private def transformStat(exprOwner: Symbol)(stat: Tree): List[Tree] = { + + private def Thicket(trees: List[Tree]) = Block(trees, EmptyTree) + override def transform(stat: Tree): Tree = { val clazz = currentOwner val statSym = stat.symbol - // println(s"transformStat $statSym in ${exprOwner.ownerChain}") - // currentRun.trackerFactory.snapshot() - /* For traits, the getter has the val's RHS, which is already constant-folded. There is no valdef. For classes, we still have the classic scheme of private[this] valdef + getter & setter that read/assign to the field. @@ -396,54 +492,58 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor */ stat match { // TODO: consolidate with ValDef case - case stat@DefDef(_, _, _, _, _, rhs) if (statSym hasFlag ACCESSOR) && !excludedAccessorOrFieldByFlags(statSym) => - /* TODO: defer replacing ConstantTyped tree by the corresponding constant until erasure - (until then, trees should not be constant-folded -- only their type tracks the resulting constant) - TODO: also remove ACCESSOR flag since there won't be an underlying field to access? - */ - def statInlinedConstantRhs = - if (clazz.isTrait) stat // we've already done this for traits.. the asymmetry will be solved by the above todo - else deriveDefDef(stat)(_ => gen.mkAttributedQualifier(rhs.tpe)) - - if (rhs ne EmptyTree) { - val fieldMemoization = fieldMemoizationIn(statSym, clazz) - - // if we decide to have non-stored fields with initialization effects, the stat's RHS should be replaced by unit - // if (!fieldMemoization.stored) deriveUnitDef(stat) else stat - - if (fieldMemoization.pureConstant) statInlinedConstantRhs :: Nil - else super.transform(stat) :: Nil - } else { - stat :: Nil + // TODO: defer replacing ConstantTyped tree by the corresponding constant until erasure + // (until then, trees should not be constant-folded -- only their type tracks the resulting constant) + // also remove ACCESSOR flag since there won't be an underlying field to access? + case DefDef(_, _, _, _, _, rhs) if (statSym hasFlag ACCESSOR) + && (rhs ne EmptyTree) && !excludedAccessorOrFieldByFlags(statSym) + && !clazz.isTrait // we've already done this for traits.. the asymmetry will be solved by the above todo + && fieldMemoizationIn(statSym, clazz).pureConstant => + deriveDefDef(stat)(_ => gen.mkAttributedQualifier(rhs.tpe)) // TODO: recurse? + + // drop the val for (a) constant (pure & not-stored) and (b) not-stored (but still effectful) fields + case ValDef(mods, _, _, rhs) if (rhs ne EmptyTree) && !excludedAccessorOrFieldByFlags(statSym) + && fieldMemoizationIn(statSym, clazz).pureConstant => + EmptyTree + + case ModuleDef(_, _, impl) => + // ??? The typer doesn't take kindly to seeing this ClassDef; we have to set NoType so it will be ignored. + val cd = super.transform(ClassDef(statSym.moduleClass, impl) setType NoType) + if (clazz.isClass) cd + else { // local module -- symbols cannot be generated by info transformer, so do it all here + val moduleVar = newModuleVarSymbol(currentOwner, statSym, statSym.info.resultType, 0) + Thicket(cd :: mkField(moduleVar) :: mkAccessor(statSym)(moduleInit(statSym)) :: Nil) } - case stat@ValDef(mods, _, _, rhs) if !excludedAccessorOrFieldByFlags(statSym) => - if (rhs ne EmptyTree) { - val fieldMemoization = fieldMemoizationIn(statSym, clazz) - - // drop the val for (a) constant (pure & not-stored) and (b) not-stored (but still effectful) fields - if (fieldMemoization.pureConstant) Nil // (a) - else super.transform(stat) :: Nil // if (fieldMemoization.stored) - // else rhsAtOwner(transformStat, exprOwner) :: Nil // (b) -- not used currently - } else { - stat :: Nil - } + case tree => + super.transform(tree) - - case tree => List( - if (exprOwner != currentOwner && tree.isTerm) atOwner(exprOwner)(super.transform(tree)) - else super.transform(tree) - ) } } - // TODO flatMapConserve or something like it - // TODO use thicket encoding of multi-tree transformStat? - // if (!currentOwner.isClass || currentOwner.isPackageClass || currentOwner.isInterface) stats flatMap transformStat(exprOwner) // for the ModuleDef case, the only top-level case in that method - // else - override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = - afterOwnPhase { - fieldsAndAccessors(exprOwner) ++ (stats flatMap transformStat(exprOwner)) - } + def transformTermsAtExprOwner(exprOwner: Symbol)(stat: Tree) = + if (stat.isTerm) atOwner(exprOwner)(transform(stat)) + else transform(stat) + + override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { + val addedStats = + if (exprOwner.isLocalDummy) afterOwnPhase { fieldsAndAccessors(exprOwner.owner) } + else Nil + + val newStats = + stats mapConserve (if (exprOwner != currentOwner) transformTermsAtExprOwner(exprOwner) else transform) + + addedStats ::: (if (newStats eq stats) stats else { + // check whether we need to flatten thickets and drop empty ones + if (newStats exists { case EmptyTree => true case Block(_, EmptyTree) => true case _ => false }) + newStats flatMap { + case EmptyTree => Nil + case Block(thicket, EmptyTree) => thicket + case stat => stat :: Nil + } + else newStats + }) + } + } } diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index d98daf0ffb..0033736dbe 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -319,7 +319,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } else if (mixinMember.hasAllFlags(METHOD | MODULE) && mixinMember.hasNoFlags(LIFTED | BRIDGE)) { // mixin objects: todo what happens with abstract objects? - addMember(clazz, mixinMember.cloneSymbol(clazz, mixinMember.flags & ~(DEFERRED | lateDEFERRED)) setPos clazz.pos) + // addMember(clazz, mixinMember.cloneSymbol(clazz, mixinMember.flags & ~(DEFERRED | lateDEFERRED)) setPos clazz.pos) } else if (mixinMember.hasFlag(ACCESSOR) && notDeferredOrLate(mixinMember) && (mixinMember hasFlag (LAZY | PARAMACCESSOR)) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 0eae1ce419..46ad4b35a1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -93,8 +93,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans rtp1 <:< rtp2 case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) => rtp1 <:< rtp2 - case (TypeRef(_, sym, _), _) if sym.isModuleClass => + + // all this module business would be so much simpler if we moduled^w modelled a module as a class and an accessor, like we do for fields + case (TypeRef(_, sym, _), _) if sym.isModuleClass => overridesTypeInPrefix(NullaryMethodType(tp1), tp2, prefix, isModuleOverride) + case (_, TypeRef(_, sym, _)) if sym.isModuleClass => + overridesTypeInPrefix(tp1, NullaryMethodType(tp2), prefix, isModuleOverride) + case _ => def classBoundAsSeen(tp: Type) = tp.typeSymbol.classBound.asSeenFrom(prefix, tp.typeSymbol.owner) (tp1 <:< tp2) || isModuleOverride && ( @@ -1182,69 +1187,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans finally popLevel() } - /** Eliminate ModuleDefs. In all cases the ModuleDef (carrying a module symbol) is - * replaced with a ClassDef (carrying the corresponding module class symbol) with additional - * trees created as follows: - * - * 1) A statically reachable object (either top-level or nested only in objects) receives - * no additional trees. - * 2) An inner object which matches an existing member (e.g. implements an interface) - * receives an accessor DefDef to implement the interface. - * 3) An inner object otherwise receives a private ValDef which declares a module var - * (the field which holds the module class - it has a name like Foo$module) and an - * accessor for that field. The instance is created lazily, on first access. - */ - private def eliminateModuleDefs(moduleDef: Tree): List[Tree] = exitingRefchecks { - val ModuleDef(_, _, impl) = moduleDef - val module = moduleDef.symbol - val site = module.owner - val moduleName = module.name.toTermName - // The typer doesn't take kindly to seeing this ClassDef; we have to - // set NoType so it will be ignored. - val cdef = ClassDef(module.moduleClass, impl) setType NoType - - def matchingInnerObject() = { - val newFlags = (module.flags | STABLE) & ~MODULE - val newInfo = NullaryMethodType(module.moduleClass.tpe) - val accessor = site.newMethod(moduleName, module.pos, newFlags) setInfoAndEnter newInfo - - DefDef(accessor, Select(This(site), module)) :: Nil - } - val newTrees = cdef :: ( - if (module.isStatic) - // trait T { def f: Object }; object O extends T { object f }. Need to generate method f in O. - if (module.isOverridingSymbol) matchingInnerObject() else Nil - else - newInnerObject(site, module) - ) - transformTrees(newTrees map localTyper.typedPos(moduleDef.pos)) - } - def newInnerObject(site: Symbol, module: Symbol): List[Tree] = { - if (site.isTrait) - DefDef(module, EmptyTree) :: Nil - else { - val moduleVar = site newModuleVarSymbol module - // used for the mixin case: need a new symbol owned by the subclass for the accessor, rather than repurposing the module symbol - def mkAccessorSymbol = - site.newMethod(module.name.toTermName, site.pos, STABLE | MODULE | MIXEDIN) - .setInfo(moduleVar.tpe) - .andAlso(self => if (module.isPrivate) self.expandName(module.owner)) - - val accessor = if (module.owner == site) module else mkAccessorSymbol - val accessorDef = DefDef(accessor, gen.mkAssignAndReturn(moduleVar, gen.newModule(module, moduleVar.tpe)).changeOwner(moduleVar -> accessor)) - - ValDef(moduleVar) :: accessorDef :: Nil - } - } - def mixinModuleDefs(clazz: Symbol): List[Tree] = { - val res = for { - mixinClass <- clazz.mixinClasses.iterator - module <- mixinClass.info.decls.iterator.filter(_.isModule) - newMember <- newInnerObject(clazz, module) - } yield transform(localTyper.typedPos(clazz.pos)(newMember)) - res.toList - } def transformStat(tree: Tree, index: Int): List[Tree] = tree match { case t if treeInfo.isSelfConstrCall(t) => @@ -1255,7 +1198,6 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans debuglog("refsym = " + currentLevel.refsym) reporter.error(currentLevel.refpos, "forward reference not allowed from self constructor invocation") } - case ModuleDef(_, _, _) => eliminateModuleDefs(tree) case ValDef(_, _, _, _) => val tree1 = transform(tree) // important to do before forward reference check if (tree1.symbol.isLazy) tree1 :: Nil @@ -1702,13 +1644,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans checkOverloadedRestrictions(currentOwner, currentOwner) // SI-7870 default getters for constructors live in the companion module checkOverloadedRestrictions(currentOwner, currentOwner.companionModule) - val bridges = addVarargBridges(currentOwner) - val moduleDesugared = if (currentOwner.isTrait) Nil else mixinModuleDefs(currentOwner) + val bridges = addVarargBridges(currentOwner) // TODO: do this during uncurry? checkAllOverrides(currentOwner) checkAnyValSubclass(currentOwner) if (currentOwner.isDerivedValueClass) currentOwner.primaryConstructor makeNotPrivate NoSymbol // SI-6601, must be done *after* pickler! - if (bridges.nonEmpty || moduleDesugared.nonEmpty) deriveTemplate(tree)(_ ::: bridges ::: moduleDesugared) else tree + if (bridges.nonEmpty) deriveTemplate(tree)(_ ::: bridges) else tree case dc@TypeTreeWithDeferredRefCheck() => abort("adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc") case tpt@TypeTree() => @@ -1821,7 +1762,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } result match { case ClassDef(_, _, _, _) - | TypeDef(_, _, _, _) => + | TypeDef(_, _, _, _) + | ModuleDef(_, _, _) => if (result.symbol.isLocalToBlock || result.symbol.isTopLevel) varianceValidator.traverse(result) case tt @ TypeTree() if tt.original != null => diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index af1cdafcda..1456022b10 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -324,17 +324,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def newImport(pos: Position): TermSymbol = newTermSymbol(nme.IMPORT, pos) - def newModuleVarSymbol(accessor: Symbol): TermSymbol = { - val newName = nme.moduleVarName(accessor.name.toTermName) - val newFlags = MODULEVAR | ( if (this.isClass) PrivateLocal | SYNTHETIC else 0 ) - val newInfo = thisType.memberType(accessor).finalResultType - val mval = newVariable(newName, accessor.pos.focus, newFlags.toLong) addAnnotation VolatileAttr - - if (this.isClass) - mval setInfoAndEnter newInfo - else - mval setInfo newInfo - } final def newModuleSymbol(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleSymbol = newTermSymbol(name, pos, newFlags).asInstanceOf[ModuleSymbol] @@ -3262,7 +3251,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * returned, otherwise, `NoSymbol` is returned. */ protected final def companionModule0: Symbol = - flatOwnerInfo.decl(name.toTermName).suchThat(sym => sym.isModuleNotMethod && (sym isCoDefinedWith this)) + flatOwnerInfo.decl(name.toTermName).suchThat(sym => sym.isModule && (sym isCoDefinedWith this)) override def companionModule = companionModule0 override def companionSymbol = companionModule0 diff --git a/src/reflect/scala/reflect/internal/Variances.scala b/src/reflect/scala/reflect/internal/Variances.scala index 69bade55f1..bc8a5de119 100644 --- a/src/reflect/scala/reflect/internal/Variances.scala +++ b/src/reflect/scala/reflect/internal/Variances.scala @@ -167,7 +167,9 @@ trait Variances { case ClassDef(_, _, _, _) | TypeDef(_, _, _, _) => validateVariance(sym) super.traverse(tree) - // ModuleDefs need not be considered because they have been eliminated already + case ModuleDef(_, _, _) => + validateVariance(sym.moduleClass) + super.traverse(tree) case ValDef(_, _, _, _) => validateVariance(sym) case DefDef(_, _, tparams, vparamss, _, _) => diff --git a/test/files/neg/t0764.check b/test/files/neg/t0764.check index 0c7cff1e1e..830278e715 100644 --- a/test/files/neg/t0764.check +++ b/test/files/neg/t0764.check @@ -1,5 +1,5 @@ t0764.scala:13: error: type mismatch; - found : Node{type T = _1.type} where val _1: Node{type T = NextType} + found : Node{type T = _2.type} where val _2: Node{type T = NextType} required: Node{type T = Main.this.AType} (which expands to) Node{type T = Node{type T = NextType}} new Main[AType]( (value: AType).prepend ) diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala index d24b4e518b..9217183c74 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala @@ -83,7 +83,7 @@ class ScalaInlineInfoTest extends BytecodeTesting { false, // final class None, // not a sam Map( - ("O()LT$O$;", MethodInlineInfo(true ,false,false)), // the accessor is abstract in bytecode, but still effectivelyFinal because there's no (late)DEFERRED flag, https://github.com/scala/scala-dev/issues/126 + ("O()LT$O$;", MethodInlineInfo(false,false,false)), ("T$$super$toString()Ljava/lang/String;", MethodInlineInfo(true ,false,false)), ("T$_setter_$x1_$eq(I)V", MethodInlineInfo(false,false,false)), ("f1()I", MethodInlineInfo(false,false,false)), -- cgit v1.2.3 From e26b4f49d80caa8f71a1986f604cca7f4714e3c3 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 26 May 2016 20:10:47 -0700 Subject: Uncurry's info transform: non-static module --> method We do this during uncurry so we can insert the necessary applications to the empty argument list. Fields is too late. Refchecks is no longer an info transform. --- src/compiler/scala/tools/nsc/Global.scala | 2 +- .../scala/tools/nsc/transform/Erasure.scala | 2 +- .../scala/tools/nsc/transform/UnCurry.scala | 7 ++++-- .../scala/tools/nsc/typechecker/RefChecks.scala | 26 ++-------------------- .../scala/tools/reflect/ReflectGlobal.scala | 3 +-- src/reflect/scala/reflect/internal/Phase.scala | 4 +++- src/reflect/scala/reflect/internal/Types.scala | 6 ++--- .../reflect/internal/transform/RefChecks.scala | 14 ------------ .../reflect/internal/transform/Transforms.scala | 5 +---- .../scala/reflect/internal/transform/UnCurry.scala | 7 +++++- test/files/run/t6240-universe-code-gen.scala | 2 +- 11 files changed, 24 insertions(+), 54 deletions(-) (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index c2d92ce7f9..af866e1a6f 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -456,7 +456,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } with Pickler // phaseName = "refchecks" - override object refChecks extends { + object refChecks extends { val global: Global.this.type = Global.this val runsAfter = List("pickler") val runsRightAfter = None diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 289ac0cc02..dc62b40578 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -508,7 +508,7 @@ abstract class Erasure extends AddInterfaces // If `member` is a ModuleSymbol, the bridge should not also be a ModuleSymbol. Otherwise we // end up with two module symbols with the same name in the same scope, which is surprising // when implementing later phases. - if (member.isModule) newFlags = (newFlags | METHOD) & ~(MODULE | lateMETHOD | STABLE) + if (member.isModule) newFlags = (newFlags | METHOD) & ~(MODULE | STABLE) val bridge = other.cloneSymbolImpl(root, newFlags) setPos root.pos debuglog("generating bridge from %s (%s): %s to %s: %s".format( diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 374e8430d8..a337ab7359 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -510,8 +510,11 @@ abstract class UnCurry extends InfoTransform case MethodType(_, _) => tree case tp => tree setType MethodType(Nil, tp.resultType) } - if (tree.symbol.isMethod && !tree.tpe.isInstanceOf[PolyType]) - gen.mkApplyIfNeeded(removeNullary()) + val sym = tree.symbol + // our info transformer may not have run yet, so duplicate flag logic instead of forcing it to run + val isMethodExitingUncurry = (sym hasFlag METHOD) || (sym hasFlag MODULE) && !sym.isStatic + if (isMethodExitingUncurry && !tree.tpe.isInstanceOf[PolyType]) + gen.mkApplyIfNeeded(removeNullary()) // apply () if tree.tpe has zero-arg MethodType else if (tree.isType) TypeTree(tree.tpe) setPos tree.pos else diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 46ad4b35a1..7021e12f1a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -14,7 +14,7 @@ import scala.tools.nsc.settings.ScalaVersion import scala.tools.nsc.settings.NoScalaVersion import symtab.Flags._ -import transform.InfoTransform +import transform.Transform /**

@@ -43,7 +43,7 @@ import transform.InfoTransform * * @todo Check whether we always check type parameter bounds. */ -abstract class RefChecks extends InfoTransform with scala.reflect.internal.transform.RefChecks { +abstract class RefChecks extends Transform { val global: Global // need to repeat here because otherwise last mixin defines global as // SymbolTable. If we had DOT this would not be an issue @@ -54,31 +54,9 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans /** the following two members override abstract members in Transform */ val phaseName: String = "refchecks" - override def phaseNewFlags: Long = lateMETHOD def newTransformer(unit: CompilationUnit): RefCheckTransformer = new RefCheckTransformer(unit) - override def changesBaseClasses = false - - override def transformInfo(sym: Symbol, tp: Type): Type = { - // !!! This is a sketchy way to do things. - // It would be better to replace the module symbol with a method symbol - // rather than creating this module/method hybrid which must be special - // cased all over the place. Look for the call sites which use(d) some - // variation of "isMethod && !isModule", which to an observer looks like - // a nonsensical condition. (It is now "isModuleNotMethod".) - if (sym.isModule && !sym.isStatic) { - sym setFlag lateMETHOD | STABLE - // Note that this as far as we can see it works equally well - // to set the METHOD flag here and dump lateMETHOD, but it does - // mean that under separate compilation the typer will see - // modules as methods (albeit stable ones with singleton types.) - // So for now lateMETHOD lives while we try to convince ourselves - // we can live without it or deliver that info some other way. - log(s"Stabilizing module method for ${sym.fullLocationString}") - } - super.transformInfo(sym, tp) - } val toJavaRepeatedParam = new SubstSymMap(RepeatedParamClass -> JavaRepeatedParamClass) val toScalaRepeatedParam = new SubstSymMap(JavaRepeatedParamClass -> RepeatedParamClass) diff --git a/src/compiler/scala/tools/reflect/ReflectGlobal.scala b/src/compiler/scala/tools/reflect/ReflectGlobal.scala index e30d1ed7cd..b80524df2b 100644 --- a/src/compiler/scala/tools/reflect/ReflectGlobal.scala +++ b/src/compiler/scala/tools/reflect/ReflectGlobal.scala @@ -30,8 +30,7 @@ class ReflectGlobal(currentSettings: Settings, reporter: Reporter, override val override def transformedType(sym: Symbol) = postErasure.transformInfo(sym, erasure.transformInfo(sym, - uncurry.transformInfo(sym, - refChecks.transformInfo(sym, sym.info)))) + uncurry.transformInfo(sym, sym.info))) override def isCompilerUniverse = true diff --git a/src/reflect/scala/reflect/internal/Phase.scala b/src/reflect/scala/reflect/internal/Phase.scala index f56c41d71c..eb193adbf2 100644 --- a/src/reflect/scala/reflect/internal/Phase.scala +++ b/src/reflect/scala/reflect/internal/Phase.scala @@ -47,7 +47,9 @@ abstract class Phase(val prev: Phase) { final val specialized: Boolean = ((prev ne null) && (prev ne NoPhase)) && (prev.name == "specialize" || prev.specialized) final val refChecked: Boolean = ((prev ne null) && (prev ne NoPhase)) && (prev.name == "refchecks" || prev.refChecked) - // are we past the fields phase, so that we should allow writing to vals (as part of type checking trait setters) + // are we past the fields phase, so that: + // - we should allow writing to vals (as part of type checking trait setters) + // - modules have module accessors final val assignsFields: Boolean = ((prev ne null) && (prev ne NoPhase)) && (prev.name == "fields" || prev.assignsFields) /** This is used only in unsafeTypeParams, and at this writing is diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index fb78aa5009..7dda805378 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -3469,10 +3469,10 @@ trait Types if (!sym.isOverridableMember || sym.owner == pre.typeSymbol) sym else pre.nonPrivateMember(sym.name).suchThat { sym => // SI-7928 `isModuleNotMethod` is here to avoid crashing with spuriously "overloaded" module accessor and module symbols. - // These appear after refchecks eliminates ModuleDefs that implement an interface. + // These appear after the fields phase eliminates ModuleDefs that implement an interface. // Here, we exclude the module symbol, which allows us to bind to the accessor. - // SI-8054 We must only do this after refchecks, otherwise we exclude the module symbol which does not yet have an accessor! - val isModuleWithAccessor = phase.refChecked && sym.isModuleNotMethod + // SI-8054 We must only do this after fields, otherwise we exclude the module symbol which does not yet have an accessor! + val isModuleWithAccessor = phase.assignsFields && sym.isModuleNotMethod sym.isType || (!isModuleWithAccessor && sym.isStable && !sym.hasVolatileType) } orElse sym } diff --git a/src/reflect/scala/reflect/internal/transform/RefChecks.scala b/src/reflect/scala/reflect/internal/transform/RefChecks.scala index 4ca114e781..e69de29bb2 100644 --- a/src/reflect/scala/reflect/internal/transform/RefChecks.scala +++ b/src/reflect/scala/reflect/internal/transform/RefChecks.scala @@ -1,14 +0,0 @@ -package scala -package reflect -package internal -package transform - -trait RefChecks { - - val global: SymbolTable - import global._ - - def transformInfo(sym: Symbol, tp: Type): Type = - if (sym.isModule && !sym.isStatic) NullaryMethodType(tp) - else tp -} diff --git a/src/reflect/scala/reflect/internal/transform/Transforms.scala b/src/reflect/scala/reflect/internal/transform/Transforms.scala index 0d2f355aa5..de5bfbd39a 100644 --- a/src/reflect/scala/reflect/internal/transform/Transforms.scala +++ b/src/reflect/scala/reflect/internal/transform/Transforms.scala @@ -23,12 +23,10 @@ trait Transforms { self: SymbolTable => } } - private val refChecksLazy = new Lazy(new { val global: Transforms.this.type = self } with RefChecks) private val uncurryLazy = new Lazy(new { val global: Transforms.this.type = self } with UnCurry) private val erasureLazy = new Lazy(new { val global: Transforms.this.type = self } with Erasure) private val postErasureLazy = new Lazy(new { val global: Transforms.this.type = self } with PostErasure) - def refChecks = refChecksLazy.force def uncurry = uncurryLazy.force def erasure = erasureLazy.force def postErasure = postErasureLazy.force @@ -36,8 +34,7 @@ trait Transforms { self: SymbolTable => def transformedType(sym: Symbol) = postErasure.transformInfo(sym, erasure.transformInfo(sym, - uncurry.transformInfo(sym, - refChecks.transformInfo(sym, sym.info)))) + uncurry.transformInfo(sym, sym.info))) def transformedType(tpe: Type) = postErasure.elimErasedValueType(erasure.scalaErasure(uncurry.uncurry(tpe))) diff --git a/src/reflect/scala/reflect/internal/transform/UnCurry.scala b/src/reflect/scala/reflect/internal/transform/UnCurry.scala index 85e3ac60e8..a50084f40d 100644 --- a/src/reflect/scala/reflect/internal/transform/UnCurry.scala +++ b/src/reflect/scala/reflect/internal/transform/UnCurry.scala @@ -83,5 +83,10 @@ trait UnCurry { * @MAT: starting with this phase, the info of every symbol will be normalized */ def transformInfo(sym: Symbol, tp: Type): Type = - if (sym.isType) uncurryType(tp) else uncurry(tp) + if (sym.isType) uncurryType(tp) + else if ((sym hasFlag MODULE) && !sym.isStatic) { // see Fields::nonStaticModuleToMethod + sym setFlag METHOD | STABLE + MethodType(Nil, uncurry(tp)) + } + else uncurry(tp) } diff --git a/test/files/run/t6240-universe-code-gen.scala b/test/files/run/t6240-universe-code-gen.scala index 60e1f76b54..80b60bab7e 100644 --- a/test/files/run/t6240-universe-code-gen.scala +++ b/test/files/run/t6240-universe-code-gen.scala @@ -54,7 +54,7 @@ object Test extends App { | |${forceCode("this", JavaUniverseTpe)} |${forceCode("definitions", DefinitionsModule.info)} - |${forceCode("refChecks", typeOf[scala.reflect.internal.transform.RefChecks])} + | |${forceCode("uncurry", typeOf[scala.reflect.internal.transform.UnCurry])} |${forceCode("erasure", typeOf[scala.reflect.internal.transform.Erasure])} | } -- cgit v1.2.3 From 8f792280630721bdc1e6ee9199eb0cf8cb035fce Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Sun, 29 May 2016 21:45:08 -0700 Subject: Simplify erasure + mixin Remove some old, obsolete & untested hacks from ExplicitOuter. Added a test for one of them to show this is now fine. There are a lot of `makeNotPrivate` invocations sprinkled around the codebase. Lets see if we can centralize the ones dealing with trait methods that need implementations in the phase that emits them. For example Fields (accessors for fields/modules) or SuperAccessors. --- .../scala/tools/nsc/transform/AddInterfaces.scala | 94 -------------- .../scala/tools/nsc/transform/Erasure.scala | 59 +++++++-- .../scala/tools/nsc/transform/ExplicitOuter.scala | 105 +++++---------- .../scala/tools/nsc/transform/Fields.scala | 3 + src/compiler/scala/tools/nsc/transform/Mixin.scala | 142 +++++++++------------ .../scala/reflect/internal/Definitions.scala | 6 +- .../scala/reflect/internal/transform/Erasure.scala | 14 +- test/files/run/t2946/MyResponseCommon_2.scala | 7 + test/files/run/t2946/ResponseCommon_1.scala | 13 ++ .../nsc/backend/jvm/opt/ScalaInlineInfoTest.scala | 2 +- 10 files changed, 177 insertions(+), 268 deletions(-) create mode 100644 test/files/run/t2946/MyResponseCommon_2.scala create mode 100644 test/files/run/t2946/ResponseCommon_1.scala (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala index 406832c262..e69de29bb2 100644 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -1,94 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Martin Odersky - */ - -package scala.tools.nsc -package transform - -import symtab._ -import Flags._ - -abstract class AddInterfaces extends InfoTransform { self: Erasure => - import global._ // the global environment - import definitions._ // standard classes and methods - - def transformMixinInfo(tp: Type): Type = tp match { - case ClassInfoType(parents, decls, clazz) if clazz.isPackageClass || !clazz.isJavaDefined => - - val parents1 = parents match { - case Nil => Nil - case hd :: tl => - assert(!hd.typeSymbol.isTrait, clazz) - if (clazz.isTrait) ObjectTpe :: tl - else parents - } - if (clazz.isTrait) { - decls foreach { sym => - if (!sym.isType) sym.info // initialize to set lateMETHOD flag if necessary - } - } - if (parents1 eq parents) tp - else ClassInfoType(parents1, decls, clazz) - case _ => - tp - } - -// Tree transformation -------------------------------------------------------------- - private class ChangeOwnerAndReturnTraverser(oldowner: Symbol, newowner: Symbol) - extends ChangeOwnerTraverser(oldowner, newowner) { - override def traverse(tree: Tree) { - tree match { - case _: Return => change(tree.symbol) - case _ => - } - super.traverse(tree) - } - } - - /** Add calls to supermixin constructors - * `super[mix].$init$()` - * to tree, which is assumed to be the body of a constructor of class clazz. - */ - private def addMixinConstructorCalls(tree: Tree, clazz: Symbol): Tree = { - def mixinConstructorCall(mc: Symbol): Tree = atPos(tree.pos) { - Apply(SuperSelect(clazz, mc.primaryConstructor), Nil) - } - val mixinConstructorCalls: List[Tree] = { - for (mc <- clazz.mixinClasses.reverse - if mc.isTrait && mc.primaryConstructor != NoSymbol) - yield mixinConstructorCall(mc) - } - tree match { - - case Block(Nil, expr) => - // AnyVal constructor - have to provide a real body so the - // jvm doesn't throw a VerifyError. But we can't add the - // body until now, because the typer knows that Any has no - // constructor and won't accept a call to super.init. - assert((clazz isSubClass AnyValClass) || clazz.info.parents.isEmpty, clazz) - Block(List(Apply(gen.mkSuperInitCall, Nil)), expr) - - case Block(stats, expr) => - // needs `hasSymbolField` check because `supercall` could be a block (named / default args) - val (presuper, supercall :: rest) = stats span (t => t.hasSymbolWhich(_ hasFlag PRESUPER)) - treeCopy.Block(tree, presuper ::: (supercall :: mixinConstructorCalls ::: rest), expr) - } - } - - protected val mixinTransformer = new Transformer { - override def transform(tree: Tree): Tree = { - val sym = tree.symbol - val tree1 = tree match { - case DefDef(_,_,_,_,_,_) if sym.isClassConstructor && sym.isPrimaryConstructor && sym.owner != ArrayClass => - deriveDefDef(tree)(addMixinConstructorCalls(_, sym.owner)) // (3) - case Template(parents, self, body) => - val parents1 = sym.owner.info.parents map (t => TypeTree(t) setPos tree.pos) - treeCopy.Template(tree, parents1, noSelfType, body) - case _ => - tree - } - super.transform(tree1) - } - } -} diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index f3fd7c5f67..d190802f66 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -12,7 +12,7 @@ import symtab._ import Flags._ import scala.reflect.internal.Mode._ -abstract class Erasure extends AddInterfaces +abstract class Erasure extends InfoTransform with scala.reflect.internal.transform.Erasure with typechecker.Analyzer with TypingTransformers @@ -373,16 +373,53 @@ abstract class Erasure extends AddInterfaces class UnknownSig extends Exception - /** The symbol's erased info. This is the type's erasure, except for the following symbols: - * - * - For $asInstanceOf : [T]T - * - For $isInstanceOf : [T]scala#Boolean - * - For class Array : [T]C where C is the erased classinfo of the Array class. - * - For Array[T]. : {scala#Int)Array[T] - * - For a type parameter : A type bounds type consisting of the erasures of its bounds. - */ - override def transformInfo(sym: Symbol, tp: Type): Type = - transformMixinInfo(super.transformInfo(sym, tp)) + // TODO: move to constructors? + object mixinTransformer extends Transformer { + /** Add calls to supermixin constructors + * `super[mix].$init$()` + * to tree, which is assumed to be the body of a constructor of class clazz. + */ + private def addMixinConstructorCalls(tree: Tree, clazz: Symbol): Tree = { + def mixinConstructorCall(mc: Symbol): Tree = atPos(tree.pos) { + Apply(SuperSelect(clazz, mc.primaryConstructor), Nil) + } + val mixinConstructorCalls: List[Tree] = { + for (mc <- clazz.mixinClasses.reverse + if mc.isTrait && mc.primaryConstructor != NoSymbol) + yield mixinConstructorCall(mc) + } + tree match { + + case Block(Nil, expr) => + // AnyVal constructor - have to provide a real body so the + // jvm doesn't throw a VerifyError. But we can't add the + // body until now, because the typer knows that Any has no + // constructor and won't accept a call to super.init. + assert((clazz isSubClass AnyValClass) || clazz.info.parents.isEmpty, clazz) + Block(List(Apply(gen.mkSuperInitCall, Nil)), expr) + + case Block(stats, expr) => + // needs `hasSymbolField` check because `supercall` could be a block (named / default args) + val (presuper, supercall :: rest) = stats span (t => t.hasSymbolWhich(_ hasFlag PRESUPER)) + treeCopy.Block(tree, presuper ::: (supercall :: mixinConstructorCalls ::: rest), expr) + } + } + + override def transform(tree: Tree): Tree = { + val sym = tree.symbol + val tree1 = tree match { + case DefDef(_,_,_,_,_,_) if sym.isClassConstructor && sym.isPrimaryConstructor && sym.owner != ArrayClass => + deriveDefDef(tree)(addMixinConstructorCalls(_, sym.owner)) // (3) + case Template(parents, self, body) => + val parents1 = sym.owner.info.parents map (t => TypeTree(t) setPos tree.pos) + treeCopy.Template(tree, parents1, noSelfType, body) + case _ => + tree + } + super.transform(tree1) + } + } + val deconstMap = new TypeMap { // For some reason classOf[Foo] creates ConstantType(Constant(tpe)) with an actual Type for tpe, diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 411ff6b9be..f3d5ceb0f0 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -158,13 +158,6 @@ abstract class ExplicitOuter extends InfoTransform case MethodType(params, resTp) => val resTpTransformed = transformInfo(sym, resTp) - // juggle flags (and mangle names) after transforming info - if (sym.owner.isTrait) { - // TODO: I don't believe any private accessors remain after the fields phase - if ((sym hasFlag (ACCESSOR | SUPERACCESSOR)) || sym.isModule) sym.makeNotPrivate(sym.owner) // 5 - if (sym.isProtected) sym setFlag notPROTECTED // 6 - } - val paramsWithOuter = if (sym.isClassConstructor && isInner(sym.owner)) // 1 sym.newValueParameter(nme.OUTER_ARG, sym.pos).setInfo(sym.owner.outerClass.thisType) :: params @@ -202,14 +195,6 @@ abstract class ExplicitOuter extends InfoTransform if (restp eq restp1) tp else PolyType(tparams, restp1) case _ => - // Local fields of traits need to be unconditionally unprivatized. - // Reason: Those fields might need to be unprivatized if referenced by an inner class. - // On the other hand, mixing in the trait into a separately compiled - // class needs to have a common naming scheme, independently of whether - // the field was accessed from an inner class or not. See #2946 - if (sym.owner.isTrait && sym.isLocalToThis && - (sym.getterIn(sym.owner) == NoSymbol)) - sym.makeNotPrivate(sym.owner) tp } @@ -300,61 +285,41 @@ abstract class ExplicitOuter extends InfoTransform } } - /**

- * The phase performs the following transformations on terms: - *

- *
    - *
  1. - *

    - * An class which is not an interface and is not static gets an outer - * accessor (@see outerDefs). - *

    - *

    - * 1a. A class which is not a trait gets an outer field. - *

    - *
  2. - *
  3. - * A constructor of a non-trait inner class gets an outer parameter. - *
  4. - *
  5. - * A reference C.this where C refers to an - * outer class is replaced by a selection - * this.$outer$$C1 ... .$outer$$Cn (@see outerPath) - *
  6. - *
  7. - *
  8. - *
  9. - * A call to a constructor Q.(args) or Q.$init$(args) where Q != this and - * the constructor belongs to a non-static class is augmented by an outer argument. - * E.g. Q.(OUTER, args) where OUTER - * is the qualifier corresponding to the singleton type Q. - *
  10. - *
  11. - * A call to a constructor this.(args) in a - * secondary constructor is augmented to this.(OUTER, args) - * where OUTER is the last parameter of the secondary constructor. - *
  12. - *
  13. - * Remove private modifier from class members M - * that are accessed from an inner class. - *
  14. - *
  15. - * Remove protected modifier from class members M - * that are accessed without a super qualifier accessed from an inner - * class or trait. - *
  16. - *
  17. - * Remove private and protected modifiers - * from type symbols - *
  18. - *
  19. - * Remove private modifiers from members of traits - *
  20. - *
- *

- * Note: The whole transform is run in phase explicitOuter.next. - *

- */ + /** The phase performs the following transformations (more or less...): + * + * (1) An class which is not an interface and is not static gets an outer accessor (@see outerDefs). + * (1a) A class which is not a trait gets an outer field. + * + * (4) A constructor of a non-trait inner class gets an outer parameter. + * + * (5) A reference C.this where C refers to an outer class is replaced by a selection + * `this.$outer$$C1 ... .$outer$$Cn` (@see outerPath) + * + * (7) A call to a constructor Q.(args) or Q.$init$(args) where Q != this and + * the constructor belongs to a non-static class is augmented by an outer argument. + * E.g. Q.(OUTER, args) where OUTER + * is the qualifier corresponding to the singleton type Q. + * + * (8) A call to a constructor this.(args) in a + * secondary constructor is augmented to this.(OUTER, args) + * where OUTER is the last parameter of the secondary constructor. + * + * (9) Remove private modifier from class members M that are accessed from an inner class. + * + * (10) Remove protected modifier from class members M that are accessed + * without a super qualifier accessed from an inner class or trait. + * + * (11) Remove private and protected modifiers from type symbols + * + * Note: The whole transform is run in phase explicitOuter.next. + * + * TODO: Make this doc reflect what's actually going on. + * Some of the deviations are motivated by separate compilation + * (name mangling based on usage is inherently unstable). + * Now that traits are compiled 1:1 to interfaces, they can have private members, + * so there's also less need to make trait members non-private + * (they still may need to be implemented in subclasses, though we could make those protected...). + */ class ExplicitOuterTransformer(unit: CompilationUnit) extends OuterPathTransformer(unit) { transformer => diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala index a9ed8e3aca..1900fcdc16 100644 --- a/src/compiler/scala/tools/nsc/transform/Fields.scala +++ b/src/compiler/scala/tools/nsc/transform/Fields.scala @@ -53,6 +53,8 @@ import symtab.Flags._ * An overridden val's side-effect is still performed. * The only change due to overriding is that its value is never written to the field * (the overridden val's value is, of course, stored in the field in addition to its side-effect being performed). + * + * TODO: check init support (or drop the -Xcheck-init flag??) */ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransformers { @@ -247,6 +249,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor val accessorUnderConsideration = !(member hasFlag (DEFERRED | LAZY)) // destructively mangle accessor's name (which may cause rehashing of decls), also sets flags + // TODO: technically, only necessary for stored fields if (member hasFlag PRIVATE) member makeNotPrivate clazz // Need to mark as notPROTECTED, so that it's carried over to the synthesized member in subclasses, diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 441ae625d0..a1441fe7b3 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -19,6 +19,22 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { /** The name of the phase: */ val phaseName: String = "mixin" + /** Some trait methods need to be implemented in subclasses, so they cannot be private. + * + * They may be protected, now that traits are compiled 1:1 to interfaces. + * + * TODO: interfaces can also have private members, so there's also less need to make trait members non-private + * can we leave more methods private? + * (they still may need to be implemented in subclasses, though we could make those protected...). + */ + def publicizeTraitMethod(sym: Symbol): Unit = { + if ((sym hasFlag PRIVATE) && + ( (sym hasFlag SUPERACCESSOR) // super accessors by definition must be implemented in a subclass, so can't have the private (TODO: why are they ever private in a trait to begin with!?!?) + || (sym hasFlag ACCESSOR | MODULE))) // an accessor / module *may* need to be implemented in a subclass, and thus cannot be private + sym.makeNotPrivate(sym.owner) + + if (sym hasFlag PROTECTED) sym setFlag notPROTECTED + } /** This map contains a binding (class -> info) if * the class with this info at phase mixinPhase has been treated for mixin composition @@ -43,7 +59,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * methods in the impl class (because they can have arbitrary initializers) */ private def isImplementedStatically(sym: Symbol) = ( - sym.isMethod + (sym.isMethod || ((sym hasFlag MODULE) && !sym.isStatic)) && notDeferred(sym) && sym.owner.isTrait && (!sym.isModule || sym.hasFlag(PRIVATE | LIFTED)) @@ -221,6 +237,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } clazz.info.decls.unlink(member) } + else if (member.isMethod) publicizeTraitMethod(member) } debuglog("new defs of " + clazz + " = " + clazz.info.decls) } @@ -318,10 +335,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { superAccessor.asInstanceOf[TermSymbol] setAlias alias1 } } - else if (mixinMember.hasAllFlags(METHOD | MODULE) && mixinMember.hasNoFlags(LIFTED | BRIDGE)) { - // mixin objects: todo what happens with abstract objects? - // addMember(clazz, mixinMember.cloneSymbol(clazz, mixinMember.flags & ~DEFERRED) setPos clazz.pos) - } else if (mixinMember.hasFlag(ACCESSOR) && notDeferred(mixinMember) && (mixinMember hasFlag (LAZY | PARAMACCESSOR)) && !isOverriddenAccessor(mixinMember, clazz.info.baseClasses)) { @@ -866,109 +879,70 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def getterBody(getter: Symbol) = { assert(getter.isGetter) - val readValue = getter.tpe match { - // A field "final val f = const" in a trait generates a getter with a ConstantType. - case MethodType(Nil, ConstantType(c)) => - Literal(c) - case _ => - // if it is a mixed-in lazy value, complete the accessor - if (getter.isLazy) { - val isUnit = isUnitGetter(getter) - val initCall = Apply(SuperSelect(clazz, initializer(getter)), Nil) - val selection = fieldAccess(getter) - val init = if (isUnit) initCall else atPos(getter.pos)(Assign(selection, initCall)) - val returns = if (isUnit) UNIT else selection - mkLazyDef(clazz, getter, List(init), returns, fieldOffset(getter)) + val readValue = + if (getter.isLazy) { + getter.tpe.resultType match { + case ConstantType(c) => Literal(c) + case _ => + val initCall = Apply(SuperSelect(clazz, initializer(getter)), Nil) + val offset = fieldOffset(getter) + if (isUnitGetter(getter)) mkLazyDef(clazz, getter, List(initCall), UNIT, offset) + else mkLazyDef(clazz, getter, List(atPos(getter.pos)(Assign(fieldAccess(getter), initCall))), fieldAccess(getter), offset) } - // For a field of type Unit in a trait, no actual field is generated when being mixed in. - else if (isUnitGetter(getter)) UNIT - else fieldAccess(getter) - } + } else { + assert(getter.hasFlag(PARAMACCESSOR)) + fieldAccess(getter) + } + if (!needsInitFlag(getter)) readValue else mkCheckedAccessor(clazz, readValue, fieldOffset(getter), getter.pos, getter) } def setterBody(setter: Symbol) = { val getter = setter.getterIn(clazz) - - // A trait with a field of type Unit creates a trait setter (invoked by the - // implementation class constructor), like for any other trait field. - // However, no actual field is created in the class that mixes in the trait. - // Therefore the setter does nothing (except setting the -Xcheckinit flag). + assert(getter.hasFlag(PARAMACCESSOR), s"missing implementation for non-paramaccessor $setter in $clazz") val setInitFlag = if (!needsInitFlag(getter)) Nil else List(mkSetFlag(clazz, fieldOffset(getter), getter, bitmapKind(getter))) - val fieldInitializer = - if (isUnitGetter(getter)) Nil - else List(Assign(fieldAccess(setter), Ident(setter.firstParam))) - - (fieldInitializer ::: setInitFlag) match { - case Nil => UNIT - // If there's only one statement, the Block factory does not actually create a Block. - case stats => Block(stats: _*) - } + Block(Assign(fieldAccess(setter), Ident(setter.firstParam)) :: setInitFlag : _*) } def fieldAccess(accessor: Symbol) = Select(This(clazz), accessor.accessed) - def isOverriddenSetter(sym: Symbol) = - nme.isTraitSetterName(sym.name) && { - val other = sym.nextOverriddenSymbol - isOverriddenAccessor(other.getterIn(other.owner), clazz.info.baseClasses) - } - // for all symbols `sym` in the class definition, which are mixed in: + // for all symbols `sym` in the class definition, which are mixed in by mixinTraitMembers for (sym <- clazz.info.decls ; if sym hasFlag MIXEDIN) { // if current class is a trait, add an abstract method for accessor `sym` - if (clazz.isTrait) { - addDefDef(sym) - } else { - // if class is not a trait add accessor definitions - if (sym.hasFlag(ACCESSOR) && !sym.hasFlag(DEFERRED)) { - assert(sym hasFlag (LAZY | PARAMACCESSOR), s"mixed in $sym from $clazz is not lazy/param?!?") - - // add accessor definitions - addDefDef(sym, { - if (sym.isSetter) { - // If this is a setter of a mixed-in field which is overridden by another mixin, - // the trait setter of the overridden one does not need to do anything - the - // trait setter of the overriding field will initialize the field. - if (isOverriddenSetter(sym)) UNIT - else setterBody(sym) - } - else getterBody(sym) - }) - } - else if (sym.isModule && !(sym hasFlag LIFTED | BRIDGE)) { - // Moved to Refchecks - } - else if (!sym.isMethod) { - // add fields - addValDef(sym) - } - else if (sym.isSuperAccessor) { - // add superaccessors - addDefDef(sym) - } - else { - // add forwarders - assert(sym.alias != NoSymbol, (sym, sym.debugFlagString, clazz)) - // debuglog("New forwarder: " + sym.defString + " => " + sym.alias.defString) - if (!sym.isMacro) addDefDef(sym, Apply(SuperSelect(clazz, sym.alias), sym.paramss.head.map(Ident(_)))) - } + // ditto for a super accessor (will get an RHS in completeSuperAccessor) + if (clazz.isTrait || sym.isSuperAccessor) addDefDef(sym) + // implement methods mixed in from a supertrait (the symbols were created by mixinTraitMembers) + else if (sym.hasFlag(ACCESSOR) && !sym.hasFlag(DEFERRED)) { + assert(sym hasFlag (LAZY | PARAMACCESSOR), s"mixed in $sym from $clazz is not lazy/param?!?") + + // add accessor definitions + addDefDef(sym, if (sym.isSetter) setterBody(sym) else getterBody(sym)) + } + else if (!sym.isMethod) addValDef(sym) // field + else if (!sym.isMacro) { // forwarder + assert(sym.alias != NoSymbol, (sym, sym.debugFlagString, clazz)) + // debuglog("New forwarder: " + sym.defString + " => " + sym.alias.defString) + addDefDef(sym, Apply(SuperSelect(clazz, sym.alias), sym.paramss.head.map(Ident(_)))) } } + stats1 = add(stats1, newDefs.toList) - if (clazz.isTrait) stats1 = - stats1.filter { + + if (clazz.isTrait) stats1 = stats1.filter { case vd: ValDef => - // TODO do we get here? + assert(vd.symbol.hasFlag(PRESUPER | PARAMACCESSOR | LAZY), s"unexpected valdef $vd in trait $clazz") false case _ => true } + if (!clazz.isTrait) stats1 = stats1 map completeSuperAccessor + stats1 } @@ -989,8 +963,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { /** The transform that gets applied to a tree after it has been completely * traversed and possible modified by a preTransform. * This step will - * - change every node type that refers to an implementation class to its - * corresponding interface, unless the node's symbol is an implementation class. * - change parents of templates to conform to parents in the symbol info * - add all new definitions to a class or interface * - remove widening casts @@ -998,8 +970,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * to static calls of methods in implementation modules (@see staticCall) * - change super calls to methods in implementation classes to static calls * (@see staticCall) - * - change `this` in implementation modules to references to the self parameter - * - refer to fields in some implementation class via an abstract method in the interface. */ private def postTransform(tree: Tree): Tree = { val sym = tree.symbol @@ -1020,6 +990,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { treeCopy.Template(tree, parents1, self, statsWithNewDefs) case Select(qual, name) if sym.owner.isTrait && !sym.isMethod => + assert(sym.hasFlag(PARAMACCESSOR | PRESUPER), s"!!! Unexpected reference to field $sym in trait $currentOwner") + // refer to fields in some trait an abstract getter in the interface. val ifaceGetter = sym getterIn sym.owner diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 35ec80901e..eca1bbea5a 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -1040,11 +1040,7 @@ trait Definitions extends api.StandardDefinitions { } } - /** Remove references to class Object (other than the head) in a list of parents */ - def removeLaterObjects(tps: List[Type]): List[Type] = tps match { - case Nil => Nil - case x :: xs => x :: xs.filterNot(_.typeSymbol == ObjectClass) - } + /** Remove all but one reference to class Object from a list of parents. */ def removeRedundantObjects(tps: List[Type]): List[Type] = tps match { case Nil => Nil diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index 412c49f571..62ca50d035 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -148,9 +148,19 @@ trait Erasure { apply(atp) case ClassInfoType(parents, decls, clazz) => ClassInfoType( - if (clazz == ObjectClass || isPrimitiveValueClass(clazz)) Nil + if (clazz == ObjectClass || isPrimitiveValueClass(clazz) || parents.isEmpty) Nil else if (clazz == ArrayClass) ObjectTpe :: Nil - else removeLaterObjects(parents map this), + else { + val erasedParents = parents map this + + // drop first parent for traits -- it has been normalized to a class by now, + // but we should drop that in bytecode + val firstParent = + if (clazz.hasFlag(Flags.TRAIT) && !clazz.hasFlag(Flags.JAVA)) ObjectTpe + else erasedParents.head + + firstParent :: erasedParents.tail.filter(_.typeSymbol != ObjectClass) + }, decls, clazz) case _ => mapOver(tp) diff --git a/test/files/run/t2946/MyResponseCommon_2.scala b/test/files/run/t2946/MyResponseCommon_2.scala new file mode 100644 index 0000000000..4f8f924f2c --- /dev/null +++ b/test/files/run/t2946/MyResponseCommon_2.scala @@ -0,0 +1,7 @@ +class MyResponseCommon extends Parser with ResponseCommon + +object Test { + def main(args: Array[String]) { + new MyResponseCommon + } +} diff --git a/test/files/run/t2946/ResponseCommon_1.scala b/test/files/run/t2946/ResponseCommon_1.scala new file mode 100644 index 0000000000..bb921e7027 --- /dev/null +++ b/test/files/run/t2946/ResponseCommon_1.scala @@ -0,0 +1,13 @@ +class Parser { + def parse(t: Any): Unit = {} +} + +trait ResponseCommon extends Parser { + private[this] var paramsParser: Parser = null + def withParamsParser(parser: Parser) = {paramsParser = parser; this} + + override abstract def parse(t: Any): Unit = t match { + case ("params", value: List[_]) => value.foreach {paramsParser.parse(_)} + case _ => super.parse(t) + } +} diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala index 9217183c74..e03b703dc9 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala @@ -106,7 +106,7 @@ class ScalaInlineInfoTest extends BytecodeTesting { ("x5()I", MethodInlineInfo(true, false,false)), ("x5$(LT;)I", MethodInlineInfo(true ,false,false)), ("L$lzycompute$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true, false,false)), - ("L$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true ,false,false)), + ("T$$L$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true ,false,false)), ("nest$1()I", MethodInlineInfo(true, false,false)), ("$init$(LT;)V", MethodInlineInfo(true,false,false))), None // warning -- cgit v1.2.3 From 9b59f5f9530d54c917479c6bf44aa3007ba0a2df Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 6 Jul 2016 16:11:32 -0700 Subject: Allow 'overriding' deferred var Discovered by scala-js's test suite. --- src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 2 +- test/files/neg/trait_fields_var_override.check | 5 +++++ test/files/neg/trait_fields_var_override.scala | 2 ++ test/files/pos/trait_fields_var_override_deferred.scala | 2 ++ 4 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 test/files/neg/trait_fields_var_override.check create mode 100644 test/files/neg/trait_fields_var_override.scala create mode 100644 test/files/pos/trait_fields_var_override_deferred.scala (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 7021e12f1a..674e0051b4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -433,7 +433,7 @@ abstract class RefChecks extends Transform { } else if (other.isAbstractOverride && other.isIncompleteIn(clazz) && !member.isAbstractOverride) { overrideError("needs `abstract override' modifiers") } - else if (member.isAnyOverride && (other hasFlag ACCESSOR) && !(other hasFlag STABLE)) { + else if (member.isAnyOverride && (other hasFlag ACCESSOR) && !(other hasFlag STABLE | DEFERRED)) { // The check above used to look at `field` == `other.accessed`, ensuring field.isVariable && !field.isLazy, // which I think is identical to the more direct `!(other hasFlag STABLE)` (given that `other` is a method). // Also, we're moving away from (looking at) underlying fields (vals in traits no longer have them, to begin with) diff --git a/test/files/neg/trait_fields_var_override.check b/test/files/neg/trait_fields_var_override.check new file mode 100644 index 0000000000..7245c78b09 --- /dev/null +++ b/test/files/neg/trait_fields_var_override.check @@ -0,0 +1,5 @@ +trait_fields_var_override.scala:2: error: overriding variable end in trait SizeChangeEvent of type Int; + variable end cannot override a mutable variable +class BackedUpListIterator[E](override protected var end: Int) extends SizeChangeEvent + ^ +one error found diff --git a/test/files/neg/trait_fields_var_override.scala b/test/files/neg/trait_fields_var_override.scala new file mode 100644 index 0000000000..f61ba09eec --- /dev/null +++ b/test/files/neg/trait_fields_var_override.scala @@ -0,0 +1,2 @@ +trait SizeChangeEvent { protected var end: Int = 1 } +class BackedUpListIterator[E](override protected var end: Int) extends SizeChangeEvent diff --git a/test/files/pos/trait_fields_var_override_deferred.scala b/test/files/pos/trait_fields_var_override_deferred.scala new file mode 100644 index 0000000000..0205326506 --- /dev/null +++ b/test/files/pos/trait_fields_var_override_deferred.scala @@ -0,0 +1,2 @@ +trait SizeChangeEvent { protected var end: Int } +class BackedUpListIterator[E](override protected var end: Int) extends SizeChangeEvent -- cgit v1.2.3 From 3540ffc1fb81eef75aeff41a0ba9142b1cce8a53 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Sun, 17 Jul 2016 22:04:46 -0700 Subject: Admit @volatile on accessor in trait There's no other place to squirrel away the annotation until we create a field in a subclass. The test documents the idea, but does not capture the regression seen in the wild, as explained in a comment. --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 4 +++- test/files/pos/trait_fields_volatile.scala | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/trait_fields_volatile.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 efb0830204..1fe8438b56 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2008,7 +2008,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val tpt1 = checkNoEscaping.privates(sym, typedType(vdef.tpt)) checkNonCyclic(vdef, tpt1) - if (sym.hasAnnotation(definitions.VolatileAttr) && !sym.isMutable) + // allow trait accessors: it's the only vehicle we have to hang on to annotations that must be passed down to + // the field that's mixed into a subclass + if (sym.hasAnnotation(definitions.VolatileAttr) && !((sym hasFlag MUTABLE) || (sym hasFlag ACCESSOR) && sym.owner.isTrait)) VolatileValueError(vdef) val rhs1 = diff --git a/test/files/pos/trait_fields_volatile.scala b/test/files/pos/trait_fields_volatile.scala new file mode 100644 index 0000000000..030b24f187 --- /dev/null +++ b/test/files/pos/trait_fields_volatile.scala @@ -0,0 +1,13 @@ +// This test illustrates the intent of what should work (but didn't for a while during the fields refactoring), +// but it does not actually defend against the regression seen in twitter-util's Scheduler, which I cannot reproduce +// outside the project. The whole project consistently fails to build before, and compiles after the commit +// that includes this test, but this single test file (as well as Scheduler.scala with external dependencies removed) +// compiles both before and after.... +// (https://github.com/twitter/util/blob/6398a56923/util-core/src/main/scala/com/twitter/concurrent/Scheduler.scala#L260-L265) +// There's also a run test that checks that the field in C is actually volatile. +trait Vola { + @volatile private[this] var _vola = "tile" + @volatile var vola = "tile" +} + +class C extends Vola -- cgit v1.2.3 From fdc94676928cd9177acfcca8eb7d669e1f4eac48 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Fri, 8 Jul 2016 17:24:23 -0700 Subject: Drive accessor synthesis from info transformer Derive/filter/propagate annotations in info transformer, don't rely on having type checked the derived trees in order to see the annotations. Use synthetics mechanism for bean accessors -- the others will soon follow. Propagate inferred tpt from valdef to accessors by setting type in right spot of synthetic tree during the info completer. No need to add trees in derivedTrees, and get rid of some overfactoring in method synthesis, now that we have joined symbol and tree creation. Preserve symbol order because tests are sensitive to it. Drop warning on potentially discarded annotations, I don't think this warrants a warning. Motivated by breaking the scala-js compiler, which relied on annotations appearing when trees are type checked. Now that ordering constraint is gone in the new encoding, we may as well finally fix annotation assignment. --- .../tools/nsc/typechecker/MethodSynthesis.scala | 541 +++++++-------------- .../scala/tools/nsc/typechecker/Namers.scala | 259 ++++++---- .../scala/tools/nsc/typechecker/Typers.scala | 2 +- src/library/scala/reflect/NameTransformer.scala | 1 + .../scala/reflect/internal/AnnotationInfos.scala | 9 - src/reflect/scala/reflect/internal/StdNames.scala | 3 +- test/files/neg/t6375.check | 27 - test/files/neg/t6375.flags | 1 - test/files/neg/t6375.scala | 67 --- .../scalacheck/quasiquotes/TypecheckedProps.scala | 2 +- .../scala/reflect/internal/PrintersTest.scala | 6 +- test/pending/neg/t6375.check | 27 + test/pending/neg/t6375.flags | 1 + test/pending/neg/t6375.scala | 67 +++ 14 files changed, 453 insertions(+), 560 deletions(-) delete mode 100644 test/files/neg/t6375.check delete mode 100644 test/files/neg/t6375.flags delete mode 100644 test/files/neg/t6375.scala create mode 100644 test/pending/neg/t6375.check create mode 100644 test/pending/neg/t6375.flags create mode 100644 test/pending/neg/t6375.scala (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 0f79bb60ed..c036a2a9b8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -116,78 +116,99 @@ trait MethodSynthesis { import NamerErrorGen._ - def enterImplicitWrapper(tree: ClassDef) { - ImplicitClassWrapper(tree).createAndEnterSymbol() + def enterImplicitWrapper(tree: ClassDef): Unit = { + enterSyntheticSym(ImplicitClassWrapper(tree).derivedTree) } - // TODO: see if we can link symbol creation & tree derivation by sharing the Field/Getter/Setter factories - // maybe we can at least reuse some variant of standardAccessors? + // trees are later created by addDerivedTrees (common logic is encapsulated in field/standardAccessors/beanAccessors) def enterGetterSetter(tree: ValDef): Unit = { - tree.symbol = - if (tree.mods.isLazy) { - val lazyValGetter = LazyValGetter(tree).createAndEnterSymbol() - enterLazyVal(tree, lazyValGetter) - } else { - val getter = Getter(tree) - val getterSym = getter.createAndEnterSymbol() - - // Create the setter if necessary. - if (getter.needsSetter) Setter(tree).createAndEnterSymbol() - - // If the getter's abstract, the tree gets the getter's symbol, - // otherwise, create a field (we have to assume the getter requires storage for now). - // NOTE: we cannot look at symbol info, since we're in the process of deriving them - // (luckily, they only matter for lazy vals, which we've ruled out in this else branch, - // and `doNotDeriveField` will skip them if `!mods.isLazy`) - if (Field.noFieldFor(tree)) getterSym setPos tree.pos // TODO: why do setPos? `createAndEnterSymbol` already gave `getterSym` the position `tree.pos.focus` - else enterStrictVal(tree) - } + val getter = Getter(tree) + val getterSym = getter.createSym + val setterSym = if (getter.needsSetter) Setter(tree).createSym else NoSymbol - enterBeans(tree) - } + // a lazy field is linked to its lazy accessor (TODO: can we do the same for field -> getter -> setter) + val fieldSym = if (Field.noFieldFor(tree)) NoSymbol else Field(tree).createSym(getterSym) - import AnnotationInfo.{mkFilter => annotationFilter} + // only one symbol can have `tree.pos`, the others must focus their position + // normally the field gets the range position, but if there is none, give it to the getter + tree.symbol = fieldSym orElse (getterSym setPos tree.pos) + + val namer = if (fieldSym != NoSymbol) namerOf(fieldSym) else namerOf(getterSym) + + // There's no reliable way to detect all kinds of setters from flags or name!!! + // A BeanSetter's name does not end in `_=` -- it does begin with "set", but so could the getter + // for a regular Scala field... TODO: can we add a flag to distinguish getter/setter accessors? + val getterCompleter = namer.accessorTypeCompleter(tree, isSetter = false) + val setterCompleter = namer.accessorTypeCompleter(tree, isSetter = true) - /** This is called for those ValDefs which addDerivedTrees ignores, but - * which might have a warnable annotation situation. - */ - private def warnForDroppedAnnotations(tree: Tree) { - val annotations = tree.symbol.initialize.annotations - val targetClass = defaultAnnotationTarget(tree) - val retained = annotations filter annotationFilter(targetClass, defaultRetention = true) + getterSym setInfo getterCompleter + setterSym andAlso (_ setInfo setterCompleter) + fieldSym andAlso (_ setInfo namer.valTypeCompleter(tree)) - annotations filterNot (retained contains _) foreach (ann => issueAnnotationWarning(tree, ann, targetClass)) + enterInScope(getterSym) + setterSym andAlso (enterInScope(_)) + fieldSym andAlso (enterInScope(_)) + + deriveBeanAccessors(tree, namer) } - private def issueAnnotationWarning(tree: Tree, ann: AnnotationInfo, defaultTarget: Symbol) { - global.reporter.warning(ann.pos, - s"no valid targets for annotation on ${tree.symbol} - it is discarded unused. " + - s"You may specify targets with meta-annotations, e.g. @($ann @${defaultTarget.name})") + + private def deriveBeanAccessors(tree: ValDef, namer: Namer): Unit = { + // TODO: can we look at the annotations symbols? (name-based introduced in 8cc477f8b6, see neg/t3403) + val hasBeanProperty = tree.mods hasAnnotationNamed tpnme.BeanPropertyAnnot + val hasBoolBP = tree.mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot + + if (hasBeanProperty || hasBoolBP) { + if (!tree.name.charAt(0).isLetter) BeanPropertyAnnotationFieldWithoutLetterError(tree) + // avoids name clashes with private fields in traits + else if (tree.mods.isPrivate) BeanPropertyAnnotationPrivateFieldError(tree) + + val derivedPos = tree.pos.focus + val missingTpt = tree.tpt.isEmpty + + def deriveBeanAccessor(prefix: String): Symbol = { + val isSetter = prefix == "set" + val name = newTermName(prefix + tree.name.toString.capitalize) + val setterParam = nme.syntheticParamName(1) + + // note: tree.tpt may be EmptyTree, which will be a problem when use as the tpt of a parameter + // the completer will patch this up (we can't do this now without completing the field) + val tptToPatch = if (missingTpt) TypeTree() else tree.tpt.duplicate + + val (vparams, tpt) = + if (isSetter) (List(ValDef(Modifiers(PARAM | SYNTHETIC), setterParam, tptToPatch, EmptyTree)), TypeTree(UnitTpe)) + else (Nil, tptToPatch) + + val rhs = + if (tree.mods.isDeferred) EmptyTree + else if (isSetter) Apply(Ident(tree.name.setterName), List(Ident(setterParam))) + else Select(This(owner), tree.name) + + val sym = createMethod(tree, name, derivedPos, tree.mods.flags & BeanPropertyFlags) + context.unit.synthetics(sym) = newDefDef(sym, rhs)(tparams = Nil, vparamss = List(vparams), tpt = tpt) + sym + } + + val getterCompleter = namer.beanAccessorTypeCompleter(tree, missingTpt, isSetter = false) + enterInScope(deriveBeanAccessor(if (hasBeanProperty) "get" else "is") setInfo getterCompleter) + + if (tree.mods.isMutable) { + val setterCompleter = namer.beanAccessorTypeCompleter(tree, missingTpt, isSetter = true) + enterInScope(deriveBeanAccessor("set") setInfo setterCompleter) + } + } } + + import AnnotationInfo.{mkFilter => annotationFilter} def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match { case vd @ ValDef(mods, name, tpt, rhs) if deriveAccessors(vd) && !vd.symbol.isModuleVar => - // If we don't save the annotations, they seem to wander off. - val annotations = stat.symbol.initialize.annotations - val trees = ( - (field(vd) ::: standardAccessors(vd) ::: beanAccessors(vd)) - map (acc => atPos(vd.pos.focus)(acc derive annotations)) - filterNot (_ eq EmptyTree) - ) - // Verify each annotation landed safely somewhere, else warn. - // Filtering when isParamAccessor is a necessary simplification - // because there's a bunch of unwritten annotation code involving - // the propagation of annotations - constructor parameter annotations - // may need to make their way to parameters of the constructor as - // well as fields of the class, etc. - if (!mods.isParamAccessor) annotations foreach (ann => - if (!trees.exists(_.symbol hasAnnotation ann.symbol)) - issueAnnotationWarning(vd, ann, GetterTargetClass) - ) - - trees - case vd: ValDef => - warnForDroppedAnnotations(vd) - vd :: Nil + stat.symbol.initialize // needed! + + val getter = Getter(vd) + getter.validate() + val accessors = getter :: (if (getter.needsSetter) Setter(vd) :: Nil else Nil) + (Field(vd) :: accessors).map(_.derivedTree).filter(_ ne EmptyTree) + case cd @ ClassDef(mods, _, _, _) if mods.isImplicit => val annotations = stat.symbol.initialize.annotations // TODO: need to shuffle annotations between wrapper and class. @@ -209,194 +230,86 @@ trait MethodSynthesis { stat :: Nil } - def standardAccessors(vd: ValDef): List[DerivedFromValDef] = - if (vd.mods.isLazy) List(LazyValGetter(vd)) - else { - val getter = Getter(vd) - if (getter.needsSetter) List(getter, Setter(vd)) - else List(getter) - } - - def beanAccessors(vd: ValDef): List[DerivedFromValDef] = { - val setter = if (vd.mods.isMutable) List(BeanSetter(vd)) else Nil - if (vd.symbol hasAnnotation BeanPropertyAttr) - BeanGetter(vd) :: setter - else if (vd.symbol hasAnnotation BooleanBeanPropertyAttr) - BooleanBeanGetter(vd) :: setter - else Nil - } - def field(vd: ValDef): List[Field] = if (Field.noFieldFor(vd)) Nil else List(Field(vd)) - - /** This trait assembles what's needed for synthesizing derived methods. - * Important: Typically, instances of this trait are created TWICE for each derived - * symbol; once form Namers in an enter method, and once from Typers in addDerivedTrees. - * So it's important that creating an instance of Derived does not have a side effect, - * or if it has a side effect, control that it is done only once. - */ sealed trait Derived { - - /** The tree from which we are deriving a synthetic member. Typically, that's - * given as an argument of the instance. */ - def tree: Tree - - /** The name of the method */ - def name: TermName - - /** The flags that are retained from the original symbol */ - def flagsMask: Long - - /** The flags that the derived symbol has in addition to those retained from - * the original symbol*/ - def flagsExtra: Long - - /** type completer for the synthetic member. - */ - def completer(sym: Symbol): Type - /** The derived symbol. It is assumed that this symbol already exists and has been - * entered in the parent scope when derivedSym is called */ + * entered in the parent scope when derivedSym is called + */ def derivedSym: Symbol /** The definition tree of the derived symbol. */ def derivedTree: Tree } - sealed trait DerivedFromMemberDef extends Derived { - def tree: MemberDef - def enclClass: Symbol - - // Final methods to make the rest easier to reason about. - final def mods = tree.mods - final def basisSym = tree.symbol - final def derivedMods = mods & flagsMask | flagsExtra - } - - sealed trait DerivedFromClassDef extends DerivedFromMemberDef { - def tree: ClassDef - final def enclClass = basisSym.owner.enclClass - } - - sealed trait DerivedFromValDef extends DerivedFromMemberDef { - def tree: ValDef - final def enclClass = basisSym.enclClass - - - // There's no reliable way to detect all kinds of setters from flags or name!!! - // A BeanSetter's name does not end in `_=` -- it does begin with "set", but so could the getter - // for a regular Scala field... TODO: can we add a flag to distinguish getter/setter accessors? - final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, this.isInstanceOf[DerivedSetter]) - final def fieldSelection = Select(This(enclClass), basisSym) - - def derivedSym: Symbol = tree.symbol - def derivedTree: Tree = EmptyTree - - def isDeferred = mods.isDeferred - def validate() { } - def createAndEnterSymbol(): MethodSymbol = { - val sym = owner.newMethod(name, tree.pos.focus, derivedMods.flags) - setPrivateWithin(tree, sym) - enterInScope(sym) - sym setInfo completer(sym) - } - private def logDerived(result: Tree): Tree = { - debuglog("[+derived] " + ojoin(mods.flagString, basisSym.accurateKindString, basisSym.getterName.decode) - + " (" + derivedSym + ")\n " + result) + /** A synthetic method which performs the implicit conversion implied by + * the declaration of an implicit class. + */ + case class ImplicitClassWrapper(tree: ClassDef) extends Derived { + def derivedSym = { + val enclClass = tree.symbol.owner.enclClass + // Only methods will do! Don't want to pick up any stray + // companion objects of the same name. + val result = enclClass.info decl derivedName filter (x => x.isMethod && x.isSynthetic) + if (result == NoSymbol || result.isOverloaded) + context.error(tree.pos, s"Internal error: Unable to find the synthetic factory method corresponding to implicit class $derivedName in $enclClass / ${enclClass.info.decls}") result } - final def derive(initial: List[AnnotationInfo]): Tree = { - validate() - - // see scala.annotation.meta's package class for more info - // Annotations on ValDefs can be targeted towards the following: field, getter, setter, beanGetter, beanSetter, param. - // The defaults are: - // - (`val`-, `var`- or plain) constructor parameter annotations end up on the parameter, not on any other entity. - // - val/var member annotations solely end up on the underlying field, except in traits (@since 2.12), - // where there is no field, and the getter thus holds annotations targetting both getter & field. - // As soon as there is a field/getter (in subclasses mixing in the trait), we triage the annotations. - // - // TODO: these defaults can be surprising for annotations not meant for accessors/fields -- should we revisit? - // (In order to have `@foo val X` result in the X getter being annotated with `@foo`, foo needs to be meta-annotated with @getter) - val annotFilter: AnnotationInfo => Boolean = this match { - case _: Param => annotationFilter(ParamTargetClass, defaultRetention = true) - // By default annotations go to the field, except if the field is generated for a class parameter (PARAMACCESSOR). - case _: Field => annotationFilter(FieldTargetClass, defaultRetention = !mods.isParamAccessor) - case _: BaseGetter if owner.isTrait => annotationFilter(List(FieldTargetClass, GetterTargetClass), defaultRetention = true) - case _: BaseGetter => annotationFilter(GetterTargetClass, defaultRetention = false) - case _: Setter => annotationFilter(SetterTargetClass, defaultRetention = false) - case _: BeanSetter => annotationFilter(BeanSetterTargetClass, defaultRetention = false) - // TODO do bean getters need special treatment to collect field-targeting annotations in traits? - case _: AnyBeanGetter => annotationFilter(BeanGetterTargetClass, defaultRetention = false) - } - - // The annotations amongst those found on the original symbol which - // should be propagated to this kind of accessor. - derivedSym setAnnotations (initial filter annotFilter) + def derivedTree = factoryMeth(derivedMods, derivedName, tree) - if (derivedSym.isSetter && owner.isTrait && !isDeferred) - derivedSym addAnnotation TraitSetterAnnotationClass - - logDerived(derivedTree) - } + def derivedName = tree.name.toTermName + def derivedMods = tree.mods & AccessFlags | METHOD | IMPLICIT | SYNTHETIC } - sealed trait DerivedGetter extends DerivedFromValDef { - def needsSetter = mods.isMutable + trait DerivedAccessor extends Derived { + def tree: ValDef + def derivedName: TermName + def derivedFlags: Long + + def derivedPos = tree.pos.focus + def createSym = createMethod(tree, derivedName, derivedPos, derivedFlags) } - sealed trait DerivedSetter extends DerivedFromValDef { - protected def setterParam = derivedSym.paramss match { - case (p :: Nil) :: _ => p - case _ => NoSymbol - } - protected def setterRhs = { - assert(!derivedSym.isOverloaded, s"Unexpected overloaded setter $derivedSym for $basisSym in $enclClass") - if (Field.noFieldFor(tree) || derivedSym.isOverloaded) EmptyTree - else Assign(fieldSelection, Ident(setterParam)) - } + case class Getter(tree: ValDef) extends DerivedAccessor { + def derivedName = tree.name - private def setterDef = DefDef(derivedSym, setterRhs) - override def derivedTree: Tree = if (setterParam == NoSymbol) EmptyTree else setterDef - } + def derivedSym = + if (tree.mods.isLazy) tree.symbol.lazyAccessor + else if (Field.noFieldFor(tree)) tree.symbol + else tree.symbol.getterIn(tree.symbol.enclClass) - /** A synthetic method which performs the implicit conversion implied by - * the declaration of an implicit class. - */ - case class ImplicitClassWrapper(tree: ClassDef) extends DerivedFromClassDef { - def completer(sym: Symbol): Type = ??? // not needed - def createAndEnterSymbol(): Symbol = enterSyntheticSym(derivedTree) - def derivedSym: Symbol = { - // Only methods will do! Don't want to pick up any stray - // companion objects of the same name. - val result = enclClass.info decl name filter (x => x.isMethod && x.isSynthetic) - if (result == NoSymbol || result.isOverloaded) - context.error(tree.pos, s"Internal error: Unable to find the synthetic factory method corresponding to implicit class $name in $enclClass / ${enclClass.info.decls}") - result - } - def derivedTree: DefDef = factoryMeth(derivedMods, name, tree) - def flagsExtra: Long = METHOD | IMPLICIT | SYNTHETIC - def flagsMask: Long = AccessFlags - def name: TermName = tree.name.toTermName - } + def derivedFlags = tree.mods.flags & GetterFlags | ACCESSOR.toLong | ( if (needsSetter) 0 else STABLE ) - sealed abstract class BaseGetter(tree: ValDef) extends DerivedGetter { - def name = tree.name - def flagsMask = GetterFlags - def flagsExtra = ACCESSOR.toLong | ( if (tree.mods.isMutable) 0 else STABLE ) + def needsSetter = tree.mods.isMutable // implies !lazy - override def validate() { - assert(derivedSym != NoSymbol, tree) - if (derivedSym.isOverloaded) - GetterDefinedTwiceError(derivedSym) + override def derivedTree = + if (tree.mods.isLazy) deriveLazyAccessor + else newDefDef(derivedSym, if (Field.noFieldFor(tree)) tree.rhs else Select(This(tree.symbol.enclClass), tree.symbol))(tpt = derivedTpt) + + /** Implements lazy value accessors: + * - for lazy values of type Unit and all lazy fields inside traits, + * the rhs is the initializer itself, because we'll just "compute" the result on every access + * ("computing" unit / constant type is free -- the side-effect is still only run once, using the init bitmap) + * - for all other lazy values z the accessor is a block of this form: + * { z = ; z } where z can be an identifier or a field. + */ + private def deriveLazyAccessor: DefDef = { + val ValDef(_, _, tpt0, rhs0) = tree + val rhs1 = context.unit.transformed.getOrElse(rhs0, rhs0) + val body = + if (tree.symbol.owner.isTrait || Field.noFieldFor(tree)) rhs1 // TODO move tree.symbol.owner.isTrait into noFieldFor + else gen.mkAssignAndReturn(tree.symbol, rhs1) - super.validate() + derivedSym setPos tree.pos // cannot set it at createAndEnterSymbol because basisSym can possibly still have NoPosition + val ddefRes = DefDef(derivedSym, new ChangeOwnerTraverser(tree.symbol, derivedSym)(body)) + // ValDef will have its position focused whereas DefDef will have original correct rangepos + // ideally positions would be correct at the creation time but lazy vals are really a special case + // here so for the sake of keeping api clean we fix positions manually in LazyValGetter + ddefRes.tpt.setPos(tpt0.pos) + tpt0.setPos(tpt0.pos.focus) + ddefRes } - } - case class Getter(tree: ValDef) extends BaseGetter(tree) { - override def derivedSym = if (Field.noFieldFor(tree)) basisSym else basisSym.getterIn(enclClass) - private def derivedRhs = if (Field.noFieldFor(tree)) tree.rhs else fieldSelection // TODO: more principled approach -- this is a bit bizarre private def derivedTpt = { @@ -413,61 +326,35 @@ trait MethodSynthesis { // Range position errors ensue if we don't duplicate this in some // circumstances (at least: concrete vals with existential types.) case _: ExistentialType => TypeTree() setOriginal (tree.tpt.duplicate setPos tree.tpt.pos.focus) - case _ if isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field + case _ if tree.mods.isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field case _ => TypeTree(getterTp) } tpt setPos tree.tpt.pos.focus } - override def derivedTree: DefDef = newDefDef(derivedSym, derivedRhs)(tpt = derivedTpt) - } - /** Implements lazy value accessors: - * - for lazy values of type Unit and all lazy fields inside traits, - * the rhs is the initializer itself, because we'll just "compute" the result on every access - * ("computing" unit / constant type is free -- the side-effect is still only run once, using the init bitmap) - * - for all other lazy values z the accessor is a block of this form: - * { z = ; z } where z can be an identifier or a field. - */ - case class LazyValGetter(tree: ValDef) extends BaseGetter(tree) { - class ChangeOwnerAndModuleClassTraverser(oldowner: Symbol, newowner: Symbol) - extends ChangeOwnerTraverser(oldowner, newowner) { - - override def traverse(tree: Tree) { - tree match { - case _: DefTree => change(tree.symbol.moduleClass) - case _ => - } - super.traverse(tree) - } + def validate() = { + assert(derivedSym != NoSymbol, tree) + if (derivedSym.isOverloaded) + GetterDefinedTwiceError(derivedSym) } - // todo: in future this should be enabled but now other phases still depend on the flag for various reasons - //override def flagsMask = (super.flagsMask & ~LAZY) - override def derivedSym = basisSym.lazyAccessor - override def derivedTree: DefDef = { - val ValDef(_, _, tpt0, rhs0) = tree - val rhs1 = context.unit.transformed.getOrElse(rhs0, rhs0) - val body = - if (tree.symbol.owner.isTrait || Field.noFieldFor(tree)) rhs1 // TODO move tree.symbol.owner.isTrait into noFieldFor - else gen.mkAssignAndReturn(basisSym, rhs1) - - derivedSym setPos tree.pos // cannot set it at createAndEnterSymbol because basisSym can possibly still have NoPosition - val ddefRes = DefDef(derivedSym, new ChangeOwnerAndModuleClassTraverser(basisSym, derivedSym)(body)) - // ValDef will have its position focused whereas DefDef will have original correct rangepos - // ideally positions would be correct at the creation time but lazy vals are really a special case - // here so for the sake of keeping api clean we fix positions manually in LazyValGetter - ddefRes.tpt.setPos(tpt0.pos) - tpt0.setPos(tpt0.pos.focus) - ddefRes - } } - case class Setter(tree: ValDef) extends DerivedSetter { - def name = tree.setterName - def flagsMask = SetterFlags - def flagsExtra = ACCESSOR - // TODO: double check logic behind need for name expansion in context of new fields phase - override def derivedSym = basisSym.setterIn(enclClass) + case class Setter(tree: ValDef) extends DerivedAccessor { + def derivedName = tree.setterName + def derivedSym = tree.symbol.setterIn(tree.symbol.enclClass) + def derivedFlags = tree.mods.flags & SetterFlags | ACCESSOR + def derivedTree = + derivedSym.paramss match { + case (setterParam :: Nil) :: _ => + // assert(!derivedSym.isOverloaded, s"Unexpected overloaded setter $derivedSym for ${tree.symbol} in ${tree.symbol.enclClass}") + val rhs = + if (Field.noFieldFor(tree) || derivedSym.isOverloaded) EmptyTree + else Assign(Select(This(tree.symbol.enclClass), tree.symbol), Ident(setterParam)) + + DefDef(derivedSym, rhs) + case _ => EmptyTree + } } object Field { @@ -495,102 +382,42 @@ trait MethodSynthesis { private def traitFieldFor(vd: ValDef): Boolean = vd.mods.hasFlag(PRESUPER | LAZY) } - case class Field(tree: ValDef) extends DerivedFromValDef { - def name = tree.localName - def flagsMask = FieldFlags - def flagsExtra = PrivateLocal + case class Field(tree: ValDef) extends Derived { + private val isLazy = tree.mods.isLazy - // TODO: override def createAndEnterSymbol (currently never called on Field) - // and do `enterStrictVal(tree)`, so that enterGetterSetter and addDerivedTrees can share some logic... + // If the owner is not a class, this is a lazy val from a method, + // with no associated field. It has an accessor with $lzy appended to its name and + // its flags are set differently. The implicit flag is reset because otherwise + // a local implicit "lazy val x" will create an ambiguity with itself + // via "x$lzy" as can be seen in test #3927. + private val localLazyVal = isLazy && !owner.isClass + private val nameSuffix = + if (!localLazyVal) reflect.NameTransformer.LOCAL_SUFFIX_STRING + else reflect.NameTransformer.LAZY_LOCAL_SUFFIX_STRING - // handle lazy val first for now (we emit a Field even though we probably shouldn't...) - override def derivedTree = - if (mods.isLazy) copyValDef(tree)(mods = mods | flagsExtra, name = this.name, rhs = EmptyTree).setPos(tree.pos.focus) - else if (Field.noFieldFor(tree)) EmptyTree - else copyValDef(tree)(mods = mods | flagsExtra, name = this.name) + def derivedName = tree.name.append(nameSuffix) - } - case class Param(tree: ValDef) extends DerivedFromValDef { - def name = tree.name - def flagsMask = -1L - def flagsExtra = 0L - override def derivedTree = EmptyTree - } - def validateParam(tree: ValDef) { - Param(tree).derive(tree.symbol.annotations) - } - - sealed abstract class BeanAccessor(bean: String) extends DerivedFromValDef { - val name = newTermName(bean + tree.name.toString.capitalize) - def flagsMask = BeanPropertyFlags - def flagsExtra = 0 - override def derivedSym = enclClass.info decl name - } - sealed trait AnyBeanGetter extends BeanAccessor with DerivedGetter { - override def validate() { - if (derivedSym == NoSymbol) { - // the namer decides whether to generate these symbols or not. at that point, we don't - // have symbolic information yet, so we only look for annotations named "BeanProperty". - BeanPropertyAnnotationLimitationError(tree) - } - super.validate() + def createSym(getter: MethodSymbol) = { + val sym = owner.newValue(derivedName, tree.pos, derivedMods.flags) + if (isLazy) sym setLazyAccessor getter + sym } - } - // This trait is mixed into BooleanBeanGetter and BeanGetter by beanAccessorsFromNames, but not by beanAccessors - trait NoSymbolBeanGetter extends AnyBeanGetter { - // Derives a tree without attempting to use the original tree's symbol. - override def derivedTree = { - atPos(tree.pos.focus) { - DefDef(derivedMods mapAnnotations (_ => Nil), name, Nil, ListOfNil, tree.tpt.duplicate, - if (isDeferred) EmptyTree else Select(This(owner), tree.name) - ) - } - } - override def createAndEnterSymbol(): MethodSymbol = enterSyntheticSym(derivedTree).asInstanceOf[MethodSymbol] - } + def derivedSym = tree.symbol - // NoSymbolBeanGetter synthesizes the getter's RHS (which defers to the regular setter) - // (not sure why, but there is one use site of the BeanGetters where NoSymbolBeanGetter is not mixed in) - // TODO: clean this up... - case class BooleanBeanGetter(tree: ValDef) extends BeanAccessor("is") with AnyBeanGetter - case class BeanGetter(tree: ValDef) extends BeanAccessor("get") with AnyBeanGetter + def derivedMods = + if (!localLazyVal) tree.mods & FieldFlags | PrivateLocal | (if (isLazy) MUTABLE else 0) + else (tree.mods | ARTIFACT | MUTABLE) & ~IMPLICIT - // the bean setter's RHS delegates to the setter - case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter { - override protected def setterRhs = Apply(Ident(tree.name.setterName), List(Ident(setterParam))) - } + // TODO: why is this different from the symbol!? + private def derivedModsForTree = tree.mods | PrivateLocal - // No Symbols available. - private def beanAccessorsFromNames(tree: ValDef) = { - val ValDef(mods, _, _, _) = tree - val hasBP = mods hasAnnotationNamed tpnme.BeanPropertyAnnot - val hasBoolBP = mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot - - if (hasBP || hasBoolBP) { - val getter = ( - if (hasBP) new BeanGetter(tree) with NoSymbolBeanGetter - else new BooleanBeanGetter(tree) with NoSymbolBeanGetter - ) - getter :: { - if (mods.isMutable) List(BeanSetter(tree)) else Nil - } - } - else Nil - } + def derivedTree = + if (Field.noFieldFor(tree)) EmptyTree + else if (isLazy) copyValDef(tree)(mods = derivedModsForTree, name = derivedName, rhs = EmptyTree).setPos(tree.pos.focus) + else copyValDef(tree)(mods = derivedModsForTree, name = derivedName) - protected def enterBeans(tree: ValDef) { - val ValDef(mods, name, _, _) = tree - val beans = beanAccessorsFromNames(tree) - if (beans.nonEmpty) { - if (!name.charAt(0).isLetter) - BeanPropertyAnnotationFieldWithoutLetterError(tree) - else if (mods.isPrivate) // avoids name clashes with private fields in traits - BeanPropertyAnnotationPrivateFieldError(tree) - - // Create and enter the symbols here, add the trees in finishGetterSetter. - beans foreach (_.createAndEnterSymbol()) - } } + } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 98dca1089c..99c1b6991e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -300,7 +300,7 @@ trait Namers extends MethodSynthesis { def assignSymbol(tree: Tree): Symbol = logAssignSymbol(tree, tree match { case PackageDef(pid, _) => createPackageSymbol(tree.pos, pid) - case Import(_, _) => createImportSymbol(tree) + case imp: Import => createImportSymbol(imp) case mdef: MemberDef => createMemberSymbol(mdef, mdef.name, -1L) case _ => abort("Unexpected tree: " + tree) }) @@ -319,6 +319,12 @@ trait Namers extends MethodSynthesis { sym } + def createMethod(accessQual: MemberDef, name: TermName, pos: Position, flags: Long): MethodSymbol = { + val sym = owner.newMethod(name, pos, flags) + setPrivateWithin(accessQual, sym) + sym + } + private def logAssignSymbol(tree: Tree, sym: Symbol): Symbol = { if (isPastTyper) sym.name.toTermName match { case nme.IMPORT | nme.OUTER | nme.ANON_CLASS_NAME | nme.ANON_FUN_NAME | nme.CONSTRUCTOR => () @@ -355,11 +361,9 @@ trait Namers extends MethodSynthesis { else owner.newValue(name.toTermName, pos, flags) } } - def createFieldSymbol(tree: ValDef): TermSymbol = - owner.newValue(tree.localName, tree.pos, tree.mods.flags & FieldFlags | PrivateLocal) - def createImportSymbol(tree: Tree) = - NoSymbol.newImport(tree.pos) setInfo completerOf(tree) + def createImportSymbol(tree: Import) = + NoSymbol.newImport(tree.pos) setInfo (namerOf(tree.symbol) importTypeCompleter tree) /** All PackageClassInfoTypes come from here. */ def createPackageSymbol(pos: Position, pid: RefTree): Symbol = { @@ -632,7 +636,7 @@ trait Namers extends MethodSynthesis { } } - def completerOf(tree: Tree): TypeCompleter = { + def completerOf(tree: MemberDef): TypeCompleter = { val mono = namerOf(tree.symbol) monoTypeCompleter tree val tparams = treeInfo.typeParameters(tree) if (tparams.isEmpty) mono @@ -666,25 +670,6 @@ trait Namers extends MethodSynthesis { } } - def enterLazyVal(tree: ValDef, lazyAccessor: Symbol): TermSymbol = { - // If the owner is not a class, this is a lazy val from a method, - // with no associated field. It has an accessor with $lzy appended to its name and - // its flags are set differently. The implicit flag is reset because otherwise - // a local implicit "lazy val x" will create an ambiguity with itself - // via "x$lzy" as can be seen in test #3927. - val sym = ( - if (owner.isClass) createFieldSymbol(tree) - else owner.newValue(tree.name append nme.LAZY_LOCAL, tree.pos, (tree.mods.flags | ARTIFACT) & ~IMPLICIT) - ) - enterValSymbol(tree, sym setFlag MUTABLE setLazyAccessor lazyAccessor) - } - def enterStrictVal(tree: ValDef): TermSymbol = { - enterValSymbol(tree, createFieldSymbol(tree)) - } - def enterValSymbol(tree: ValDef, sym: TermSymbol): TermSymbol = { - enterInScope(sym) - sym setInfo namerOf(sym).monoTypeCompleter(tree) - } def enterPackage(tree: PackageDef) { val sym = assignSymbol(tree) newNamer(context.make(tree, sym.moduleClass, sym.info.decls)) enterSyms tree.stats @@ -771,7 +756,7 @@ trait Namers extends MethodSynthesis { NoSymbol } - def monoTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym => + def monoTypeCompleter(tree: MemberDef) = mkTypeCompleter(tree) { sym => // this early test is there to avoid infinite baseTypes when // adding setters and getters --> bug798 // It is a def in an attempt to provide some insulation against @@ -780,8 +765,9 @@ trait Namers extends MethodSynthesis { // on these flag checks so it can't hurt. def needsCycleCheck = sym.isNonClassType && !sym.isParameter && !sym.isExistential - // logDefinition(sym) { - val tp = typeSig(tree) + val annotations = annotSig(tree.mods.annotations) + + val tp = typeSig(tree, annotations) findCyclicalLowerBound(tp) andAlso { sym => if (needsCycleCheck) { @@ -792,42 +778,140 @@ trait Namers extends MethodSynthesis { sym.initialize } } - sym setInfo { - if (sym.isJavaDefined) RestrictJavaArraysMap(tp) - else tp - } + + sym.setInfo(if (!sym.isJavaDefined) tp else RestrictJavaArraysMap(tp)) + if (needsCycleCheck) { log(s"Needs cycle check: ${sym.debugLocationString}") if (!typer.checkNonCyclic(tree.pos, tp)) sym setInfo ErrorType } - //} validate(sym) } - def moduleClassTypeCompleter(tree: ModuleDef) = { - mkTypeCompleter(tree) { sym => - val moduleSymbol = tree.symbol - assert(moduleSymbol.moduleClass == sym, moduleSymbol.moduleClass) - moduleSymbol.info // sets moduleClass info as a side effect. - } + def moduleClassTypeCompleter(tree: ModuleDef) = mkTypeCompleter(tree) { sym => + val moduleSymbol = tree.symbol + assert(moduleSymbol.moduleClass == sym, moduleSymbol.moduleClass) + moduleSymbol.info // sets moduleClass info as a side effect. + } + + + def importTypeCompleter(imp: Import) = mkTypeCompleter(imp) { sym => + sym setInfo importSig(imp) + } + + import AnnotationInfo.{mkFilter => annotationFilter} + + def valTypeCompleter(tree: ValDef) = mkTypeCompleter(tree) { sym => + val annots = + if (tree.mods.annotations.isEmpty) Nil + else annotSig(tree.mods.annotations) filter annotationFilter(FieldTargetClass, !tree.mods.isParamAccessor) + + sym setInfo typeSig(tree, annots) + + validate(sym) } /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */ def accessorTypeCompleter(tree: ValDef, isSetter: Boolean) = mkTypeCompleter(tree) { sym => + // println(s"triaging for ${sym.debugFlagString} $sym from $valAnnots to $annots") + // typeSig calls valDefSig (because tree: ValDef) // sym is an accessor, while tree is the field (which may have the same symbol as the getter, or maybe it's the field) - val sig = accessorSigFromFieldTp(sym, isSetter, typeSig(tree)) + // TODO: can we make this work? typeSig is called on same tree (valdef) to complete info for field and all its accessors + // reuse work done in valTypeCompleter if we already computed the type signature of the val + // (assuming the field and accessor symbols are distinct -- i.e., we're not in a trait) +// val valSig = +// if ((sym ne tree.symbol) && tree.symbol.isInitialized) tree.symbol.info +// else typeSig(tree, Nil) // don't set annotations for the valdef -- we just want to compute the type sig + + val valSig = typeSig(tree, Nil) // don't set annotations for the valdef -- we just want to compute the type sig + + val sig = accessorSigFromFieldTp(sym, isSetter, valSig) + + val mods = tree.mods + if (mods.annotations.nonEmpty) { + val annotSigs = annotSig(mods.annotations) + + // neg/t3403: check that we didn't get a sneaky type alias/renamed import that we couldn't detect because we only look at names during synthesis + // (TODO: can we look at symbols earlier?) + if (!((mods hasAnnotationNamed tpnme.BeanPropertyAnnot) || (mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot)) + && annotSigs.exists(ann => (ann.matches(BeanPropertyAttr)) || ann.matches(BooleanBeanPropertyAttr))) + BeanPropertyAnnotationLimitationError(tree) + + sym setAnnotations (annotSigs filter filterAccessorAnnotations(isSetter)) + } sym setInfo pluginsTypeSigAccessor(sig, typer, tree, sym) validate(sym) } - private def accessorSigFromFieldTp(sym: global.Symbol, isSetter: Boolean, tp: global.Type): global.Type with Product with Serializable = { - if (isSetter) MethodType(List(sym.newSyntheticValueParam(tp)), UnitTpe) else NullaryMethodType(tp) + /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */ + def beanAccessorTypeCompleter(tree: ValDef, missingTpt: Boolean, isSetter: Boolean) = mkTypeCompleter(tree) { sym => + context.unit.synthetics get sym match { + case Some(ddef: DefDef) => + // sym is an accessor, while tree is the field (for traits it's actually the getter, and we're completing the setter) + // reuse work done in valTypeCompleter if we already computed the type signature of the val + // (assuming the field and accessor symbols are distinct -- i.e., we're not in a trait) + val valSig = + if ((sym ne tree.symbol) && tree.symbol.isInitialized) tree.symbol.info + else typeSig(tree, Nil) // don't set annotations for the valdef -- we just want to compute the type sig + + // patch up the accessor's tree if the valdef's tpt was not known back when the tree was synthesized + if (missingTpt) { // can't look at tree.tpt here because it may have been completed by now + if (!isSetter) ddef.tpt setType valSig + else if (ddef.vparamss.nonEmpty && ddef.vparamss.head.nonEmpty) ddef.vparamss.head.head.tpt setType valSig + else throw new TypeError(tree.pos, s"Internal error: could not complete parameter/return type for $ddef from $sym") + } + + val annots = + if (tree.mods.annotations.isEmpty) Nil + else annotSig(tree.mods.annotations) filter filterBeanAccessorAnnotations(isSetter) + + val sig = typeSig(ddef, annots) + + sym setInfo pluginsTypeSigAccessor(sig, typer, tree, sym) + + validate(sym) + + case _ => + throw new TypeError(tree.pos, s"Internal error: no synthetic tree found for bean accessor $sym") + } + } + + + // see scala.annotation.meta's package class for more info + // Annotations on ValDefs can be targeted towards the following: field, getter, setter, beanGetter, beanSetter, param. + // The defaults are: + // - (`val`-, `var`- or plain) constructor parameter annotations end up on the parameter, not on any other entity. + // - val/var member annotations solely end up on the underlying field, except in traits (@since 2.12), + // where there is no field, and the getter thus holds annotations targeting both getter & field. + // As soon as there is a field/getter (in subclasses mixing in the trait), we triage the annotations. + // + // TODO: these defaults can be surprising for annotations not meant for accessors/fields -- should we revisit? + // (In order to have `@foo val X` result in the X getter being annotated with `@foo`, foo needs to be meta-annotated with @getter) + private def filterAccessorAnnotations(isSetter: Boolean): AnnotationInfo => Boolean = + if (isSetter || !owner.isTrait) + annotationFilter(if (isSetter) SetterTargetClass else GetterTargetClass, defaultRetention = false) + else (ann => + annotationFilter(FieldTargetClass, defaultRetention = true)(ann) || + annotationFilter(GetterTargetClass, defaultRetention = true)(ann)) + + private def filterBeanAccessorAnnotations(isSetter: Boolean): AnnotationInfo => Boolean = + if (isSetter || !owner.isTrait) + annotationFilter(if (isSetter) BeanSetterTargetClass else BeanGetterTargetClass, defaultRetention = false) + else (ann => + annotationFilter(FieldTargetClass, defaultRetention = true)(ann) || + annotationFilter(BeanGetterTargetClass, defaultRetention = true)(ann)) + + + private def accessorSigFromFieldTp(sym: Symbol, isSetter: Boolean, tp: Type): Type = + if (isSetter) MethodType(List(sym.newSyntheticValueParam(tp)), UnitTpe) + else NullaryMethodType(tp) + def selfTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym => val selftpe = typer.typedType(tree).tpe sym setInfo { @@ -1539,67 +1623,52 @@ trait Namers extends MethodSynthesis { * is then assigned to the corresponding symbol (typeSig itself does not need to assign * the type to the symbol, but it can if necessary). */ - def typeSig(tree: Tree): Type = { - // log("typeSig " + tree) - /* For definitions, transform Annotation trees to AnnotationInfos, assign - * them to the sym's annotations. Type annotations: see Typer.typedAnnotated - * We have to parse definition annotations here (not in the typer when traversing - * the MemberDef tree): the typer looks at annotations of certain symbols; if - * they were added only in typer, depending on the compilation order, they may - * or may not be visible. - */ - def annotate(annotated: Symbol) = { - // typeSig might be called multiple times, e.g. on a ValDef: val, getter, setter - // parse the annotations only once. - if (!annotated.isInitialized) tree match { - case defn: MemberDef => - val ainfos = defn.mods.annotations filterNot (_ eq null) map { ann => - val ctx = typer.context - val annCtx = ctx.makeNonSilent(ann) - // need to be lazy, #1782. beforeTyper to allow inferView in annotation args, SI-5892. - AnnotationInfo lazily { - enteringTyper(newTyper(annCtx) typedAnnotation ann) - } - } - if (ainfos.nonEmpty) { - annotated setAnnotations ainfos - if (annotated.isTypeSkolem) - annotated.deSkolemize setAnnotations ainfos - } - case _ => + def typeSig(tree: Tree, annotSigs: List[AnnotationInfo]): Type = { + if (annotSigs.nonEmpty) annotate(tree.symbol, annotSigs) + + try tree match { + case member: MemberDef => createNamer(tree).memberSig(member) + case imp: Import => importSig(imp) + } catch typeErrorHandler(tree, ErrorType) + } + + /* For definitions, transform Annotation trees to AnnotationInfos, assign + * them to the sym's annotations. Type annotations: see Typer.typedAnnotated + * We have to parse definition annotations here (not in the typer when traversing + * the MemberDef tree): the typer looks at annotations of certain symbols; if + * they were added only in typer, depending on the compilation order, they may + * or may not be visible. + */ + def annotSig(annotations: List[Tree]): List[AnnotationInfo] = + annotations filterNot (_ eq null) map { ann => + val ctx = typer.context + // need to be lazy, #1782. enteringTyper to allow inferView in annotation args, SI-5892. + AnnotationInfo lazily { + enteringTyper { + newTyper(ctx.makeNonSilent(ann)) typedAnnotation ann + } } } - val sym: Symbol = tree.symbol + private def annotate(sym: Symbol, annotSigs: List[AnnotationInfo]): Unit = { + sym setAnnotations annotSigs // TODO: meta-annotations to indicate where module annotations should go (module vs moduleClass) - annotate(sym) - if (sym.isModule) annotate(sym.moduleClass) - - def getSig = tree match { - case cdef: ClassDef => - createNamer(tree).classSig(cdef) - - case mdef: ModuleDef => - createNamer(tree).moduleSig(mdef) - - case ddef: DefDef => - createNamer(tree).methodSig(ddef) - - case vdef: ValDef => - createNamer(tree).valDefSig(vdef) - - case tdef: TypeDef => - createNamer(tree).typeDefSig(tdef) //@M! + if (sym.isModule) sym.moduleClass setAnnotations annotSigs + else if (sym.isTypeSkolem) sym.deSkolemize setAnnotations annotSigs + } - case imp: Import => - importSig(imp) + // TODO OPT: move to method on MemberDef? + private def memberSig(member: MemberDef) = + member match { + case ddef: DefDef => methodSig(ddef) + case vdef: ValDef => valDefSig(vdef) + case tdef: TypeDef => typeDefSig(tdef) + case cdef: ClassDef => classSig(cdef) + case mdef: ModuleDef => moduleSig(mdef) + // skip PackageDef } - try getSig - catch typeErrorHandler(tree, ErrorType) - } - def includeParent(tpe: Type, parent: Symbol): Type = tpe match { case PolyType(tparams, restpe) => PolyType(tparams, includeParent(restpe, parent)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 1fe8438b56..f42f5bf75d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2221,7 +2221,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (!isPastTyper && meth.isPrimaryConstructor) { for (vparams <- ddef.vparamss; vd <- vparams) { if (vd.mods.isParamAccessor) { - namer.validateParam(vd) + vd.symbol setAnnotations (vd.symbol.annotations filter AnnotationInfo.mkFilter(ParamTargetClass, defaultRetention = true)) } } } diff --git a/src/library/scala/reflect/NameTransformer.scala b/src/library/scala/reflect/NameTransformer.scala index a8430548f5..ae36f5edc2 100644 --- a/src/library/scala/reflect/NameTransformer.scala +++ b/src/library/scala/reflect/NameTransformer.scala @@ -19,6 +19,7 @@ object NameTransformer { val NAME_JOIN_STRING = sys.props.getOrElse("SCALA_NAME_JOIN_STRING", "$") val MODULE_INSTANCE_NAME = "MODULE$" val LOCAL_SUFFIX_STRING = " " + val LAZY_LOCAL_SUFFIX_STRING = "$lzy" val SETTER_SUFFIX_STRING = "_$eq" val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$" diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index d58cabf3d7..cfde164754 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -175,15 +175,6 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => case (Nil, defaults) => defaults contains category case (metas, _) => metas exists (_ matches category) } - - def mkFilter(categories: List[Symbol], defaultRetention: Boolean)(ann: AnnotationInfo) = - (ann.metaAnnotations, ann.defaultTargets) match { - case (Nil, Nil) => defaultRetention - case (Nil, defaults) => categories exists defaults.contains - case (metas, _) => - val metaSyms = metas collect { case ann if !ann.symbol.isInstanceOf[StubSymbol] => ann.symbol } - categories exists (category => metaSyms exists (_ isNonBottomSubClass category)) - } } class CompleteAnnotationInfo( diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 11b5db9793..925018d3a6 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -95,6 +95,8 @@ trait StdNames { val NAME_JOIN_STRING: String = NameTransformer.NAME_JOIN_STRING val MODULE_SUFFIX_STRING: String = NameTransformer.MODULE_SUFFIX_STRING val LOCAL_SUFFIX_STRING: String = NameTransformer.LOCAL_SUFFIX_STRING + val LAZY_LOCAL_SUFFIX_STRING: String = NameTransformer.LAZY_LOCAL_SUFFIX_STRING + val TRAIT_SETTER_SEPARATOR_STRING: String = NameTransformer.TRAIT_SETTER_SEPARATOR_STRING val SINGLETON_SUFFIX: String = ".type" @@ -337,7 +339,6 @@ trait StdNames { val DEFAULT_CASE: NameType = "defaultCase$" val EQEQ_LOCAL_VAR: NameType = "eqEqTemp$" val FAKE_LOCAL_THIS: NameType = "this$" - val LAZY_LOCAL: NameType = "$lzy" val LAZY_SLOW_SUFFIX: NameType = "$lzycompute" val UNIVERSE_BUILD_PREFIX: NameType = "$u.internal.reificationSupport." val UNIVERSE_PREFIX: NameType = "$u." diff --git a/test/files/neg/t6375.check b/test/files/neg/t6375.check deleted file mode 100644 index 89d7d8060f..0000000000 --- a/test/files/neg/t6375.check +++ /dev/null @@ -1,27 +0,0 @@ -t6375.scala:6: warning: no valid targets for annotation on value x1 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @getter) - @Bippy val x1: Int // warn - ^ -t6375.scala:7: warning: no valid targets for annotation on value x2 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.field @getter) - @(Bippy @field) val x2: Int // warn - ^ -t6375.scala:9: warning: no valid targets for annotation on value x4 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.setter @getter) - @(Bippy @setter) val x4: Int // warn - ^ -t6375.scala:10: warning: no valid targets for annotation on value x5 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.param @getter) - @(Bippy @param) val x5: Int // warn - ^ -t6375.scala:20: warning: no valid targets for annotation on value q1 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.getter @field) - @(Bippy @getter) private[this] val q1: Int = 1 // warn - ^ -t6375.scala:40: warning: no valid targets for annotation on value p2 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.getter @param) - @(Bippy @getter) p2: Int, // warn - ^ -t6375.scala:41: warning: no valid targets for annotation on value p3 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.setter @param) - @(Bippy @setter) p3: Int, // warn - ^ -t6375.scala:42: warning: no valid targets for annotation on value p4 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.field @param) - @(Bippy @field) p4: Int // warn - ^ -error: No warnings can be incurred under -Xfatal-warnings. -8 warnings found -one error found diff --git a/test/files/neg/t6375.flags b/test/files/neg/t6375.flags deleted file mode 100644 index 85d8eb2ba2..0000000000 --- a/test/files/neg/t6375.flags +++ /dev/null @@ -1 +0,0 @@ --Xfatal-warnings diff --git a/test/files/neg/t6375.scala b/test/files/neg/t6375.scala deleted file mode 100644 index 21634df688..0000000000 --- a/test/files/neg/t6375.scala +++ /dev/null @@ -1,67 +0,0 @@ -import scala.annotation.meta._ - -class Bippy extends scala.annotation.StaticAnnotation - -abstract class Foo { - @Bippy val x1: Int // warn - @(Bippy @field) val x2: Int // warn - @(Bippy @getter) val x3: Int // no warn - @(Bippy @setter) val x4: Int // warn - @(Bippy @param) val x5: Int // warn -} - -object Bar extends Foo { - val x1 = 1 - val x2 = 2 - val x3 = 3 - val x4 = 4 - val x5 = 5 - - @(Bippy @getter) private[this] val q1: Int = 1 // warn - @(Bippy @getter) private val q2: Int = 1 // no warn - - def f1(@(Bippy @param) x: Int): Int = 0 // no warn - def f2(@(Bippy @getter) x: Int): Int = 0 // warn - todo - def f3(@(Bippy @setter) x: Int): Int = 0 // warn - todo - def f4(@(Bippy @field) x: Int): Int = 0 // warn - todo - def f5(@Bippy x: Int): Int = 0 // no warn - - @(Bippy @companionClass) def g1(x: Int): Int = 0 // warn - todo - @(Bippy @companionObject) def g2(x: Int): Int = 0 // warn - todo - @(Bippy @companionMethod) def g3(x: Int): Int = 0 // no warn - @Bippy def g4(x: Int): Int = 0 // no warn - - @(Bippy @companionObject @companionMethod) def g5(x: Int): Int = 0 // no warn -} - -class Dingo( - @Bippy p0: Int, // no warn - @(Bippy @param) p1: Int, // no warn - @(Bippy @getter) p2: Int, // warn - @(Bippy @setter) p3: Int, // warn - @(Bippy @field) p4: Int // warn -) - -class ValDingo( - @Bippy val p0: Int, // no warn - @(Bippy @param) val p1: Int, // no warn - @(Bippy @getter) val p2: Int, // no warn - @(Bippy @setter) val p3: Int, // warn - todo - @(Bippy @field) val p4: Int // no warn -) - -class VarDingo( - @Bippy var p0: Int, // no warn - @(Bippy @param) var p1: Int, // no warn - @(Bippy @getter) var p2: Int, // no warn - @(Bippy @setter) var p3: Int, // no warn - @(Bippy @field) var p4: Int // no warn -) - -case class CaseDingo( - @Bippy p0: Int, // no warn - @(Bippy @param) p1: Int, // no warn - @(Bippy @getter) p2: Int, // no warn - @(Bippy @setter) p3: Int, // warn - todo - @(Bippy @field) p4: Int // no warn -) diff --git a/test/files/scalacheck/quasiquotes/TypecheckedProps.scala b/test/files/scalacheck/quasiquotes/TypecheckedProps.scala index 2c4d81f333..fe07893a36 100644 --- a/test/files/scalacheck/quasiquotes/TypecheckedProps.scala +++ b/test/files/scalacheck/quasiquotes/TypecheckedProps.scala @@ -103,7 +103,7 @@ object TypecheckedProps extends QuasiquoteProperties("typechecked") val lazyName = TermName("x") val lazyRhsVal = 42 val lazyRhs = Literal(Constant(lazyRhsVal)) - val q"{lazy val $pname = $rhs}" = typecheck(q"{lazy val $lazyName = $lazyRhsVal}") + val q"{ $_ ; $mods val $pname: $_ = { $_ = $rhs ; $_ } }" = typecheck(q"{lazy val $lazyName = $lazyRhsVal}") assert(pname == lazyName) assert(rhs ≈ lazyRhs) diff --git a/test/junit/scala/reflect/internal/PrintersTest.scala b/test/junit/scala/reflect/internal/PrintersTest.scala index 6f9b711b34..234f22e9fb 100644 --- a/test/junit/scala/reflect/internal/PrintersTest.scala +++ b/test/junit/scala/reflect/internal/PrintersTest.scala @@ -1018,7 +1018,11 @@ class ValAndDefPrintTest { """, typedCode = sm""" |def a = { - | lazy val test: scala.Int = 42; + | lazy val test$$lzy: scala.Int = _; + | lazy val test: Int = { + | test$$lzy = 42; + | test$$lzy + | }; | () |}""") diff --git a/test/pending/neg/t6375.check b/test/pending/neg/t6375.check new file mode 100644 index 0000000000..89d7d8060f --- /dev/null +++ b/test/pending/neg/t6375.check @@ -0,0 +1,27 @@ +t6375.scala:6: warning: no valid targets for annotation on value x1 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @getter) + @Bippy val x1: Int // warn + ^ +t6375.scala:7: warning: no valid targets for annotation on value x2 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.field @getter) + @(Bippy @field) val x2: Int // warn + ^ +t6375.scala:9: warning: no valid targets for annotation on value x4 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.setter @getter) + @(Bippy @setter) val x4: Int // warn + ^ +t6375.scala:10: warning: no valid targets for annotation on value x5 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.param @getter) + @(Bippy @param) val x5: Int // warn + ^ +t6375.scala:20: warning: no valid targets for annotation on value q1 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.getter @field) + @(Bippy @getter) private[this] val q1: Int = 1 // warn + ^ +t6375.scala:40: warning: no valid targets for annotation on value p2 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.getter @param) + @(Bippy @getter) p2: Int, // warn + ^ +t6375.scala:41: warning: no valid targets for annotation on value p3 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.setter @param) + @(Bippy @setter) p3: Int, // warn + ^ +t6375.scala:42: warning: no valid targets for annotation on value p4 - it is discarded unused. You may specify targets with meta-annotations, e.g. @(Bippy @scala.annotation.meta.field @param) + @(Bippy @field) p4: Int // warn + ^ +error: No warnings can be incurred under -Xfatal-warnings. +8 warnings found +one error found diff --git a/test/pending/neg/t6375.flags b/test/pending/neg/t6375.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/pending/neg/t6375.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/pending/neg/t6375.scala b/test/pending/neg/t6375.scala new file mode 100644 index 0000000000..21634df688 --- /dev/null +++ b/test/pending/neg/t6375.scala @@ -0,0 +1,67 @@ +import scala.annotation.meta._ + +class Bippy extends scala.annotation.StaticAnnotation + +abstract class Foo { + @Bippy val x1: Int // warn + @(Bippy @field) val x2: Int // warn + @(Bippy @getter) val x3: Int // no warn + @(Bippy @setter) val x4: Int // warn + @(Bippy @param) val x5: Int // warn +} + +object Bar extends Foo { + val x1 = 1 + val x2 = 2 + val x3 = 3 + val x4 = 4 + val x5 = 5 + + @(Bippy @getter) private[this] val q1: Int = 1 // warn + @(Bippy @getter) private val q2: Int = 1 // no warn + + def f1(@(Bippy @param) x: Int): Int = 0 // no warn + def f2(@(Bippy @getter) x: Int): Int = 0 // warn - todo + def f3(@(Bippy @setter) x: Int): Int = 0 // warn - todo + def f4(@(Bippy @field) x: Int): Int = 0 // warn - todo + def f5(@Bippy x: Int): Int = 0 // no warn + + @(Bippy @companionClass) def g1(x: Int): Int = 0 // warn - todo + @(Bippy @companionObject) def g2(x: Int): Int = 0 // warn - todo + @(Bippy @companionMethod) def g3(x: Int): Int = 0 // no warn + @Bippy def g4(x: Int): Int = 0 // no warn + + @(Bippy @companionObject @companionMethod) def g5(x: Int): Int = 0 // no warn +} + +class Dingo( + @Bippy p0: Int, // no warn + @(Bippy @param) p1: Int, // no warn + @(Bippy @getter) p2: Int, // warn + @(Bippy @setter) p3: Int, // warn + @(Bippy @field) p4: Int // warn +) + +class ValDingo( + @Bippy val p0: Int, // no warn + @(Bippy @param) val p1: Int, // no warn + @(Bippy @getter) val p2: Int, // no warn + @(Bippy @setter) val p3: Int, // warn - todo + @(Bippy @field) val p4: Int // no warn +) + +class VarDingo( + @Bippy var p0: Int, // no warn + @(Bippy @param) var p1: Int, // no warn + @(Bippy @getter) var p2: Int, // no warn + @(Bippy @setter) var p3: Int, // no warn + @(Bippy @field) var p4: Int // no warn +) + +case class CaseDingo( + @Bippy p0: Int, // no warn + @(Bippy @param) p1: Int, // no warn + @(Bippy @getter) p2: Int, // no warn + @(Bippy @setter) p3: Int, // warn - todo + @(Bippy @field) p4: Int // no warn +) -- cgit v1.2.3