Skip to content

Defaults #25

New issue

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

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

Already on GitHub? Sign in to your account

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
10d4312
Allow BCode to emit default interface methods
retronym Jun 7, 2015
025588a
f
retronym Jul 23, 2015
0087dc7
Make FunctionN into be a functional interface
retronym Jun 8, 2015
4341983
Use FunctionN as the functional interface for indylambdas
retronym Jul 15, 2015
3304815
Remove JFunctionN
retronym Jul 15, 2015
7d49df6
Refactor flag mutation to use `setFlag`
retronym Jul 16, 2015
f73b124
Add defaults to all trait interface methods.
retronym Jul 16, 2015
41adb4d
s/DEFAULTMETHOD/JAVA_DEFAULTMETHOD/ -- see 76c133d486
adriaanm Aug 3, 2015
21b3c8e
build.xml and gitignore
adriaanm Aug 4, 2015
61249d8
assert(iface.needsImplClass) on implClass
adriaanm Aug 4, 2015
a574cdb
review notes
adriaanm Aug 4, 2015
a27c863
drop add interfaces, straight to interface from trait
adriaanm Aug 4, 2015
c3c6167
fix regression in handling of Java-defined classes
SethTisue Aug 7, 2015
459fe8f
restore some needed code from AddInterfaces
SethTisue Aug 7, 2015
55c206f
track working examples
adriaanm Aug 6, 2015
11200fc
doc
adriaanm Aug 5, 2015
4402bda
constructors shouldn't use outer in trait?
adriaanm Aug 7, 2015
6f263b8
back-end should do invokevirtual for trait method
adriaanm Aug 21, 2015
4ca72b4
makeNotPrivate should not make methods in traits final
adriaanm Aug 19, 2015
3f854b0
TODO: makeNotPrivate in explicitOuter???
adriaanm Aug 20, 2015
7a583bb
mixin's info transform
adriaanm Aug 17, 2015
178cb76
docs
adriaanm Aug 17, 2015
5358c36
Tests
adriaanm Aug 17, 2015
02ff21e
docs
adriaanm Aug 17, 2015
9cf5384
wip:mixins
adriaanm Aug 17, 2015
3e0b574
wip:new mixin: fields
adriaanm Aug 24, 2015
d6db9dd
Remove some duplication in Namers/MethodSynthesis
adriaanm Aug 6, 2015
921f321
dotty-style trait fields
adriaanm Aug 24, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
/src/intellij*/*.iml
/src/intellij*/*.ipr
/src/intellij*/*.iws
/src/intellij*/.idea
**/.cache
/.idea
/.settings
Expand Down
3 changes: 2 additions & 1 deletion build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ TODO:

<property name="dists.dir" value="${basedir}/dists"/>

<property name="copyright.string" value="Copyright 2002-2015, LAMP/EPFL"/>
<property name="copyright.string" value="Copyright 2002-2013, LAMP/EPFL"/>

<!-- These are NOT the flags used to run SuperSabbus, but the ones written
into the script runners created with scala.tools.ant.ScalaTool -->
Expand Down Expand Up @@ -763,6 +763,7 @@ TODO:
</path>

<path id="quick.bin.tool.path">
<path refid="quick.repl.build.path"/>
<path refid="quick.repl-jline.build.path"/>
<pathelement location="${build-quick.dir}/classes/scalap"/>
<pathelement location="${build-quick.dir}/classes/scaladoc"/>
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/CompilationUnits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ trait CompilationUnits { global: Global =>
* To get their sourcefiles, you need to dereference with .sourcefile
*/
private[this] val _depends = mutable.HashSet[Symbol]()
// SBT compatibility (SI-6875)
// SBT compatibility -- pre-name-hashing dependency tracking for incremental compilation in sbt (SI-6875)
//
// imagine we have a file named A.scala, which defines a trait named Foo and a module named Main
// Main contains a call to a macro, which calls compileLate to define a mock for Foo
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/scala/tools/nsc/backend/icode/Members.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package backend
package icode

import scala.collection.{ mutable, immutable }
import scala.reflect.internal.Flags
import scala.reflect.internal.util.{ SourceFile, NoSourceFile }

trait ReferenceEquality {
Expand Down Expand Up @@ -217,7 +218,7 @@ trait Members {

/** Is this method deferred ('abstract' in Java sense)?
*/
def isAbstractMethod = symbol.isDeferred || symbol.owner.isInterface || native
def isAbstractMethod = symbol.isDeferred || (symbol.owner.isInterface && !symbol.hasFlag(Flags.JAVA_DEFAULTMETHOD)) || native

def isStatic: Boolean = symbol.isStaticMember

Expand Down
16 changes: 8 additions & 8 deletions src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
Original file line number Diff line number Diff line change
Expand Up @@ -429,17 +429,17 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
// impl class after mixin. So the filter in mixin is not exactly what we need here (we
// want to identify concrete trait methods, not any accessors). So we check some symbol
// properties manually.
val traitMethodWithStaticImplementation = {
import symtab.Flags._
classSym.isTrait && !classSym.isImplClass &&
erasure.needsImplMethod(methodSym) &&
!methodSym.isModule &&
!(methodSym hasFlag (ACCESSOR | SUPERACCESSOR))
}
// val traitMethodWithStaticImplementation = {
// import symtab.Flags._
// classSym.isTrait && !classSym.isImplClass &&
// erasure.needsImplMethod(methodSym) &&
// !methodSym.isModule &&
// !(methodSym hasFlag (ACCESSOR | SUPERACCESSOR))
// }

val info = MethodInlineInfo(
effectivelyFinal = effectivelyFinal,
traitMethodWithStaticImplementation = traitMethodWithStaticImplementation,
traitMethodWithStaticImplementation = false, //traitMethodWithStaticImplementation,
annotatedInline = methodSym.hasAnnotation(ScalaInlineClass),
annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass)
)
Expand Down
10 changes: 5 additions & 5 deletions src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case Apply(fun, args) if app.hasAttachment[delambdafy.LambdaMetaFactoryCapable] =>
val attachment = app.attachments.get[delambdafy.LambdaMetaFactoryCapable].get
genLoadArguments(args, paramTKs(app))
genInvokeDynamicLambda(attachment.target, attachment.arity, attachment.functionalInterface)
genInvokeDynamicLambda(attachment.target, attachment.arity, attachment.functionalInterface, attachment.samMethod)
generatedType = asmMethodType(fun.symbol).returnType

case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isBox(fun.symbol) =>
Expand Down Expand Up @@ -1007,6 +1007,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {

def needsInterfaceCall(sym: Symbol) = (
sym.isInterface
|| sym.isTrait
|| sym.isJavaDefined && sym.isNonBottomSubClass(definitions.ClassfileAnnotationClass)
)

Expand Down Expand Up @@ -1284,7 +1285,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
def genSynchronized(tree: Apply, expectedType: BType): BType
def genLoadTry(tree: Try): BType

def genInvokeDynamicLambda(lambdaTarget: Symbol, arity: Int, functionalInterface: Symbol) {
def genInvokeDynamicLambda(lambdaTarget: Symbol, arity: Int, functionalInterface: Symbol, samMethod: Symbol) {
val isStaticMethod = lambdaTarget.hasFlag(Flags.STATIC)
def asmType(sym: Symbol) = classBTypeFromSymbol(sym).toASMType

Expand All @@ -1299,9 +1300,8 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val invokedType = asm.Type.getMethodDescriptor(asmType(functionalInterface), (receiver ::: capturedParams).map(sym => toTypeKind(sym.info).toASMType): _*)

val constrainedType = new MethodBType(lambdaParams.map(p => toTypeKind(p.tpe)), toTypeKind(lambdaTarget.tpe.resultType)).toASMType
val sam = functionalInterface.info.decls.find(_.isDeferred).getOrElse(functionalInterface.info.member(nme.apply))
val samName = sam.name.toString
val samMethodType = asmMethodType(sam).toASMType
val samName = samMethod.name.toString
val samMethodType = asmMethodType(samMethod).toASMType

val flags = 3 // TODO 2.12.x Replace with LambdaMetafactory.FLAG_SERIALIZABLE | LambdaMetafactory.FLAG_MARKERS

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -587,10 +587,10 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
}

val isNative = methSymbol.hasAnnotation(definitions.NativeAttr)
val isAbstractMethod = (methSymbol.isDeferred || methSymbol.owner.isInterface)
val isAbstractMethod = (methSymbol.isDeferred || methSymbol.owner.isInterface) && !methSymbol.hasFlag(Flags.JAVA_DEFAULTMETHOD)
val flags = GenBCode.mkFlags(
javaFlags(methSymbol),
if (claszSymbol.isInterface) asm.Opcodes.ACC_ABSTRACT else 0,
if (isAbstractMethod) asm.Opcodes.ACC_ABSTRACT else 0,
if (methSymbol.isStrictFP) asm.Opcodes.ACC_STRICT else 0,
if (isNative) asm.Opcodes.ACC_NATIVE else 0 // native methods of objects are generated in mirror classes
)
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,7 @@ abstract class BTypes {
def info_=(i: Either[NoClassBTypeInfo, ClassInfo]): Unit = {
assert(_info == null, s"Cannot set ClassBType.info multiple times: $this")
_info = i
checkInfoConsistency()
// checkInfoConsistency()
}

classBTypeFromInternalName(internalName) = this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,13 +571,13 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
import asm.Opcodes._
GenBCode.mkFlags(
if (privateFlag) ACC_PRIVATE else ACC_PUBLIC,
if (sym.isDeferred || sym.hasAbstractFlag) ACC_ABSTRACT else 0,
if (sym.isInterface) ACC_INTERFACE else 0,
if ((sym.isDeferred && !sym.hasFlag(symtab.Flags.JAVA_DEFAULTMETHOD))|| sym.hasAbstractFlag) ACC_ABSTRACT else 0,
if (sym.isInterface || sym.isTrait) ACC_INTERFACE else 0,
if (finalFlag && !sym.hasAbstractFlag) ACC_FINAL else 0,
if (sym.isStaticMember) ACC_STATIC else 0,
if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0,
if (sym.isArtifact) ACC_SYNTHETIC else 0,
if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
if (sym.isClass && !sym.isTrait) ACC_SUPER else 0,
if (sym.hasJavaEnumFlag) ACC_ENUM else 0,
if (sym.isVarargsMethod) ACC_VARARGS else 0,
if (sym.hasFlag(symtab.Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0,
Expand Down
5 changes: 3 additions & 2 deletions src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
if (sym.isStaticMember) ACC_STATIC else 0,
if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0,
if (sym.isArtifact) ACC_SYNTHETIC else 0,
if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
if (sym.isClass && !sym.isTrait) ACC_SUPER else 0,
if (sym.hasJavaEnumFlag) ACC_ENUM else 0,
if (sym.isVarargsMethod) ACC_VARARGS else 0,
if (sym.hasFlag(Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0
Expand Down Expand Up @@ -1395,10 +1395,11 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
var resTpe: asm.Type = javaType(m.symbol.tpe.resultType)
if (m.symbol.isClassConstructor)
resTpe = asm.Type.VOID_TYPE
val isAbstractTraitMeth = isJInterface && !m.symbol.hasFlag(Flags.JAVA_DEFAULTMETHOD)

val flags = mkFlags(
javaFlags(m.symbol),
if (isJInterface) asm.Opcodes.ACC_ABSTRACT else 0,
if (isAbstractTraitMeth) asm.Opcodes.ACC_ABSTRACT else 0,
if (m.symbol.isStrictFP) asm.Opcodes.ACC_STRICT else 0,
if (method.native) asm.Opcodes.ACC_NATIVE else 0, // native methods of objects are generated in mirror classes
if(isDeprecated(m.symbol)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo access flag
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>

/** Return the implementation class of a trait; create a new one of one does not yet exist */
def implClass(iface: Symbol): Symbol = {
assert(iface.needsImplClass)
iface.info

implClassMap.getOrElse(iface, enteringPhase(implClassPhase) {
Expand Down Expand Up @@ -311,7 +312,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
}
val mixinConstructorCalls: List[Tree] = {
for (mc <- clazz.mixinClasses.reverse
if mc.hasFlag(lateINTERFACE))
if mc.hasFlag(lateINTERFACE)) // implies mc.needsImplClass
yield mixinConstructorCall(implClass(mc))
}
tree match {
Expand Down
100 changes: 77 additions & 23 deletions src/compiler/scala/tools/nsc/transform/Constructors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ import symtab.Flags._

/** This phase converts classes with parameters into Java-like classes with
* fields, which are assigned to from constructors.
*
* TODO: traits
* - drop field definitions,
* - introduce trait field setters to abstract over them in the init method
*
* mixins will add back fields and implementations for the setters to
* the classes that immediately extend the given trait
*/
abstract class Constructors extends Statics with Transform with ast.TreeDSL {
import global._
Expand All @@ -21,6 +28,12 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
/** the following two members override abstract members in Transform */
val phaseName: String = "constructors"

// using lateDEFERRED for notDEFERRED for my WIP
// (a lateDEFERRED trait member should not be considered deferred during refchecks,
// as it's automatically implemented in any class inheriting the trait,
// but it should be abstract in bytecode)
override def phaseNewFlags: Long = lateDEFERRED

protected def newTransformer(unit: CompilationUnit): Transformer =
new ConstructorTransformer(unit)

Expand Down Expand Up @@ -208,7 +221,10 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
detectUsages walk auxConstructorBuf
}
}
def mustBeKept(sym: Symbol) = !omittables(sym)
def mustBeKept(sym: Symbol) = !omittable(sym)
// omit unused outers and non-memoized fields (I think we're no longer creating symbols for fields in traits, but still including backstop for now)
def omittable(sym: Symbol) = (omittables(sym) ||
(sym.isValue && !sym.isMethod && (!mustMemoize(sym) || sym.owner.isTrait))) // TODO: assert(!sym.owner.isTrait, sym.ownerChain)

} // OmittablesHelper

Expand Down Expand Up @@ -545,7 +561,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
// outer accessors become references to $outer parameter
if (canBeSupplanted(tree.symbol))
gen.mkAttributedIdent(parameter(tree.symbol.accessed)) setPos tree.pos
else if (tree.symbol.outerSource == clazz && !clazz.isImplClass)
else if (tree.symbol.outerSource == clazz && !clazz.isTrait) // TODO was !clazz.isImplClass
gen.mkAttributedIdent(parameterNamed(nme.OUTER)) setPos tree.pos
else
super.transform(tree)
Expand All @@ -570,9 +586,15 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
else transform(tree.changeOwner(oldowner -> primaryConstr.symbol))
}


// TODO: we probably can't emit an Assign tree after typers, need to Apply the setter
// Create an assignment to class field `to` with rhs `from`
def mkAssign(to: Symbol, from: Tree): Tree =
localTyper.typedPos(to.pos) { Assign(Select(This(clazz), to), from) }
localTyper.typedPos(to.pos) {
val qual = Select(This(clazz), to)
if (to.isSetter) Apply(qual, List(from))
else Assign(qual, from)
}

// Create code to copy parameter to parameter accessor field.
// If parameter is $outer, check that it is not null so that we NPE
Expand Down Expand Up @@ -623,44 +645,76 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
}
}


for (stat <- stats) {
val statSym = stat.symbol

// Move the RHS of a Val/Def (traits don't have vals, their getter holds the RHS) to the appropriate part of the ctor.
// If the val def is an early initialized or a parameter accessor,
// it goes before the superclass constructor call, otherwise it goes after.
// A lazy val's effect is not moved to the constructor, as it is delayed.
def moveEffectToCtor(mods: Modifiers, rhs: Tree) = {
val initializingRhs =
if (statSym.isLazy || rhs.isInstanceOf[Literal]) EmptyTree
else if (!mods.hasStaticFlag) intoConstructor(statSym, rhs)
else rhs

if (initializingRhs ne EmptyTree) {
val initPhase =
if (mods hasFlag STATIC) classInitStatBuf
else if (mods hasFlag PRESUPER | PARAMACCESSOR) constrPrefixBuf
else constrStatBuf

val memoized = mustMemoize(statSym)

val effect =
if (memoized) {
val assignSym =
if (statSym.isGetter) statSym.asInstanceOf[MethodSymbol].referenced //setterIn(clazz, hasExpandedName = false) // not yet expanded -- see Mixin
else statSym

assert(assignSym ne NoSymbol, stat)
mkAssign(assignSym, initializingRhs)
} else initializingRhs

initPhase += effect
memoized
} else false
}

stat match {
// recurse on class definition, store in defBuf
case _: ClassDef => defBuf += new ConstructorTransformer(unit).transform(stat)

// a concrete trait getter's RHS is treated like a ValDef
// (the actual field isn't emitted until Mixin augments the class that inherits the trait)
case DefDef(mods, _, _, _, _, rhs) if inTrait && statSym.isAccessor =>
defBuf +=
if (statSym.isGetter && (rhs ne EmptyTree)) {
val memoized = moveEffectToCtor(mods, rhs)
if (memoized) {
statSym setFlag (DEFERRED | lateDEFERRED)
deriveDefDef(dd)(_ => EmptyTree)
} else stat
} else {
if (statSym.isSetter) statSym setFlag (DEFERRED | lateDEFERRED)
stat
}

// methods (except primary constructor) go into template
// (non-primary ctors --> auxConstructorBuf / regular defs --> defBuf)
case _: DefDef if statSym.isPrimaryConstructor => ()
case _: DefDef if statSym.isConstructor => auxConstructorBuf += stat
case _: DefDef => defBuf += stat

// val defs with constant right-hand sides are eliminated.
case _: ValDef if statSym.info.isInstanceOf[ConstantType] => ()

// For all other val defs, an empty valdef goes into the template.
// Additionally, non-lazy vals are initialized by an assignment in:
// If a val needs a field, an empty valdef goes into the template.
// Except for lazy and ConstantTyped vals, the field is initialized by an assignment in:
// - the class initializer (static),
// - the constructor, before the super call (early initialized or a parameter accessor),
// - the constructor, after the super call (regular val).
case ValDef(mods, _, _, rhs) =>
val initializingRhs =
if (statSym.isLazy) EmptyTree
else if (!mods.hasStaticFlag) intoConstructor(statSym, rhs)
else rhs

if (initializingRhs ne EmptyTree) {
val initPhase =
if (mods hasFlag STATIC) classInitStatBuf
else if (mods hasFlag PRESUPER | PARAMACCESSOR) constrPrefixBuf
else constrStatBuf

initPhase += mkAssign(statSym, initializingRhs)
}
val memoized = moveEffectToCtor(mods, rhs)

defBuf += deriveValDef(stat)(_ => EmptyTree)
if (memoized) defBuf += deriveValDef(stat)(_ => EmptyTree)

// all other statements go into the constructor
case _ => constrStatBuf += intoConstructor(impl.symbol, stat)
Expand Down
Loading