Skip to content

Commit

Permalink
Merge pull request #735 from dotty-staging/ycheck-methods
Browse files Browse the repository at this point in the history
Ycheck that methods defined in ClassInfo exist in tree.
  • Loading branch information
odersky committed Aug 4, 2015
2 parents 056e124 + d528035 commit 07e24e8
Show file tree
Hide file tree
Showing 117 changed files with 137 additions and 48 deletions.
2 changes: 1 addition & 1 deletion project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ object DottyBuild extends Build {
resolvers += Resolver.sonatypeRepo("releases"),

// get libraries onboard
partestDeps := Seq("me.d-d" % "scala-compiler" % "2.11.5-20150619-173733-3bcd390afa",
partestDeps := Seq("me.d-d" % "scala-compiler" % "2.11.5-20150714-145300-2ad68448c5",
"org.scala-lang" % "scala-reflect" % scalaVersion.value,
"org.scala-lang" % "scala-library" % scalaVersion.value % "test"),
libraryDependencies ++= partestDeps.value,
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/backend/jvm/DottyBackendInterface.scala
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@ class DottyBackendInterface()(implicit ctx: Context) extends BackendInterface{
def decls: List[Symbol] = tp.decls.map(_.symbol).toList

def members: List[Symbol] =
tp.memberDenots(takeAllFilter, (name, buf) => buf ++= member(name).alternatives).map(_.symbol).toList
tp.memberDenots(takeAllFilter, (name, buf) => buf ++= tp.member(name).alternatives).map(_.symbol).toList

def typeSymbol: Symbol = tp.widenDealias.typeSymbol

Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ object Trees {

override def toText(printer: Printer) = printer.toText(this)

override def hashCode(): Int = System.identityHashCode(this)
override def hashCode(): Int = uniqueId // for debugging; was: System.identityHashCode(this)
override def equals(that: Any) = this eq that.asInstanceOf[AnyRef]
}

Expand Down
37 changes: 19 additions & 18 deletions src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -177,25 +177,26 @@ object Contexts {
/** The new implicit references that are introduced by this scope */
private var implicitsCache: ContextualImplicits = null
def implicits: ContextualImplicits = {
if (implicitsCache == null )
implicitsCache = {
val implicitRefs: List[TermRef] =
if (isClassDefContext)
try owner.thisType.implicitMembers
catch {
case ex: CyclicReference => Nil
}
else if (isImportContext) importInfo.importedImplicits
else if (isNonEmptyScopeContext) scope.implicitDecls
else Nil
val outerImplicits =
if (isImportContext && importInfo.hiddenRoot.exists)
outer.implicits exclude importInfo.hiddenRoot
else
outer.implicits
if (implicitRefs.isEmpty) outerImplicits
else new ContextualImplicits(implicitRefs, outerImplicits)(this)
if (implicitsCache == null ) {
val outerImplicits =
if (isImportContext && importInfo.hiddenRoot.exists)
outer.implicits exclude importInfo.hiddenRoot
else
outer.implicits
try
implicitsCache = {
val implicitRefs: List[TermRef] =
if (isClassDefContext) owner.thisType.implicitMembers
else if (isImportContext) importInfo.importedImplicits
else if (isNonEmptyScopeContext) scope.implicitDecls
else Nil
if (implicitRefs.isEmpty) outerImplicits
else new ContextualImplicits(implicitRefs, outerImplicits)(this)
}
catch {
case ex: CyclicReference => implicitsCache = outerImplicits
}
}
implicitsCache
}

Expand Down
9 changes: 6 additions & 3 deletions src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ object NameOps {
case nme.clone_ => nme.clone_
}

def specializedFor(returnType: Types.Type, args: List[Types.Type])(implicit ctx: Context): name.ThisName = {
def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = {

def typeToTag(tp: Types.Type): Name = {
tp.classSymbol match {
Expand All @@ -258,9 +258,12 @@ object NameOps {
}
}

val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => typeToTag(x._1))
val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => typeToTag(x._1))

name.fromName(name ++ nme.specializedTypeNames.prefix ++
args.map(typeToTag).foldRight(typeToTag(returnType))(_ ++ _) ++
nme.specializedTypeNames.suffix)
methodTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.separator ++
classTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.suffix)
}

/** If name length exceeds allowable limit, replace part of it by hash */
Expand Down
21 changes: 21 additions & 0 deletions src/dotty/tools/dotc/core/Names.scala
Original file line number Diff line number Diff line change
Expand Up @@ -348,4 +348,25 @@ object Names {
StringBuilder.newBuilder.mapResult(s => from.fromChars(s.toCharArray, 0, s.length))
def apply(): Builder[Char, Name] = termNameBuilder
}

implicit val NameOrdering: Ordering[Name] = new Ordering[Name] {
def compare(x: Name, y: Name): Int = {
if (x.isTermName && y.isTypeName) 1
else if (x.isTypeName && y.isTermName) -1
else if (x eq y) 0
else {
val until = x.length min y.length
var i = 0

while (i < until && x(i) == y(i)) i = i + 1

if (i < until) {
if (x(i) < y(i)) -1
else /*(x(i) > y(i))*/ 1
} else {
x.length - y.length
}
}
}
}
}
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,8 @@ object StdNames {
final val Void: N = "V"
final val Object: N = "L"

final val prefix: N = "$mc"
final val prefix: N = "$m"
final val separator: N = "c"
final val suffix: N = "$sp"
}

Expand Down
8 changes: 8 additions & 0 deletions src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,14 @@ object SymDenotations {
name.toTermName == nme.COMPANION_CLASS_METHOD ||
name.toTermName == nme.COMPANION_MODULE_METHOD

/** Is this a syntetic method that represents conversions between representations of a value class
* These methods are generated in ExtensionMethods
* and used in ElimErasedValueType.
*/
final def isValueClassConvertMethod(implicit ctx: Context) =
name.toTermName == nme.U2EVT ||
name.toTermName == nme.EVT2U

/** Is symbol a primitive value class? */
def isPrimitiveValueClass(implicit ctx: Context) = defn.ScalaValueClasses contains symbol

Expand Down
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,8 @@ object Symbols {
def showKind(implicit ctx: Context): String = ctx.kindString(this)
def showName(implicit ctx: Context): String = ctx.nameString(this)
def showFullName(implicit ctx: Context): String = ctx.fullNameString(this)

override def hashCode() = id // for debugging.
}

type TermSymbol = Symbol { type ThisName = TermName }
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1296,7 +1296,7 @@ object Types {
case d: SymDenotation =>
if (this.isInstanceOf[WithFixedSym]) d.current
else if (d.validFor.runId == ctx.runId || ctx.stillValid(d))
if (prefix.isTightPrefix(d.owner) || d.isConstructor) d.current
if (d.exists && prefix.isTightPrefix(d.owner) || d.isConstructor) d.current
else recomputeMember(d) // symbol could have been overridden, recompute membership
else {
val newd = loadDenot
Expand Down
3 changes: 1 addition & 2 deletions src/dotty/tools/dotc/transform/ElimErasedValueType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,11 @@ class ElimErasedValueType extends MiniPhaseTransform with InfoTransformer {

override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = {
val Apply(fun, args) = tree
val name = fun.symbol.name

// The casts to and from ErasedValueType are no longer needed once ErasedValueType
// has been eliminated.
val t =
if ((name eq nme.U2EVT) || (name eq nme.EVT2U))
if (fun.symbol.isValueClassConvertMethod)
args.head
else
tree
Expand Down
7 changes: 6 additions & 1 deletion src/dotty/tools/dotc/transform/FunctionalInterfaces.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ class FunctionalInterfaces extends MiniPhaseTransform {
val m = tree.meth.tpe.widen.asInstanceOf[MethodType]

if (shouldSpecialize(m)) {
val interfaceName = (functionName ++ m.paramTypes.length.toString).specializedFor(m.resultType, m.paramTypes)
val functionSymbol = tree.tpe.widenDealias.classSymbol
val names = ctx.atPhase(ctx.erasurePhase) {
implicit ctx => functionSymbol.typeParams.map(_.name)
}
val interfaceName = (functionName ++ m.paramTypes.length.toString).specializedFor(m.paramTypes ::: m.resultType :: Nil, names, Nil, Nil)

// symbols loaded from classpath aren't defined in periods earlier than when they where loaded
val interface = ctx.withPhase(ctx.typerPhase).getClassIfDefined(functionPackage ++ interfaceName)
if (interface.exists) {
Expand Down
22 changes: 14 additions & 8 deletions src/dotty/tools/dotc/transform/Memoize.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,20 @@ import Decorators._
override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
val sym = tree.symbol

def newField = ctx.newSymbol(
owner = ctx.owner,
name = sym.name.asTermName.fieldName,
flags = Private | (if (sym is Stable) EmptyFlags else Mutable),
info = sym.info.resultType,
coord = tree.pos)
.withAnnotationsCarrying(sym, defn.FieldMetaAnnot)
.enteredAfter(thisTransform)
def newField = {
val fieldType =
if (sym.isGetter) sym.info.resultType
else /*sym.isSetter*/ sym.info.firstParamTypes.head

ctx.newSymbol(
owner = ctx.owner,
name = sym.name.asTermName.fieldName,
flags = Private | (if (sym is Stable) EmptyFlags else Mutable),
info = fieldType,
coord = tree.pos)
.withAnnotationsCarrying(sym, defn.FieldMetaAnnot)
.enteredAfter(thisTransform)
}

/** Can be used to filter annotations on getters and setters; not used yet */
def keepAnnotations(denot: SymDenotation, meta: ClassSymbol) = {
Expand Down
8 changes: 4 additions & 4 deletions src/dotty/tools/dotc/transform/Mixin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure])

override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation =
if (sym.is(Accessor, butNot = Deferred) && sym.owner.is(Trait))
if (sym.is(Accessor, butNot = Deferred | Lazy) && sym.owner.is(Trait))
sym.copySymDenotation(initFlags = sym.flags &~ ParamAccessor | Deferred).ensureNotPrivate
else if (sym.isConstructor && sym.owner.is(Trait))
sym.copySymDenotation(
Expand All @@ -108,8 +108,8 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
sym

private def initializer(sym: Symbol)(implicit ctx: Context): TermSymbol = {
val initName = InitializerName(sym.name.asTermName)
sym.owner.info.decl(initName).symbol
val initName = if(!sym.is(Lazy)) InitializerName(sym.name.asTermName) else sym.name.asTermName
sym.owner.info.decl(initName).suchThat(_.is(Lazy) == sym.is(Lazy)).symbol
.orElse(
ctx.newSymbol(
sym.owner,
Expand Down Expand Up @@ -229,7 +229,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>

def setters(mixin: ClassSymbol): List[Tree] =
for (setter <- mixin.info.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList)
yield DefDef(implementation(setter.asTerm), unitLiteral.withPos(cls.pos))
yield transformFollowing(DefDef(implementation(setter.asTerm), unitLiteral.withPos(cls.pos)))

cpy.Template(impl)(
constr =
Expand Down
8 changes: 6 additions & 2 deletions src/dotty/tools/dotc/transform/PatternMatcher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,11 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans

def emitVars = storedBinders.nonEmpty

private lazy val (stored, substed) = (subPatBinders, subPatRefs).zipped.partition{ case (sym, _) => storedBinders(sym) }
lazy val storedSubsted = (subPatBinders, subPatRefs).zipped.partition{ case (sym, _) => storedBinders(sym) }

def stored = storedSubsted._1

def substed = storedSubsted._2

// dd: this didn't yet trigger error. But I believe it would. if this causes double denition of symbol error this can be replaced with NoRebindings
protected lazy val introducedRebindings: Rebindings = if (!emitVars) Rebindings(subPatBinders, subPatRefs)
Expand Down Expand Up @@ -1443,7 +1447,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
// require (nbSubPats > 0 && (!lastIsStar || isSeq))
protected def subPatRefs(binder: Symbol): List[Tree] = {
val refs = if (totalArity > 0 && isSeq) subPatRefsSeq(binder)
else if (totalArity > 1 && !isSeq) productElemsToN(binder, totalArity)
else if (binder.info.member(nme._1).exists && !isSeq) productElemsToN(binder, totalArity)
else ref(binder):: Nil
refs
}
Expand Down
20 changes: 20 additions & 0 deletions src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dotty.tools.dotc
package transform

import TreeTransforms._
import core.Names.Name
import core.DenotTransformers._
import core.Denotations._
import core.SymDenotations._
Expand Down Expand Up @@ -42,6 +43,12 @@ class TreeChecker extends Phase with SymTransformer {
private val seenClasses = collection.mutable.HashMap[String, Symbol]()
private val seenModuleVals = collection.mutable.HashMap[String, Symbol]()

def isValidJVMName(name: Name) =
!name.exists(c => c == '.' || c == ';' || c =='[' || c == '/')

def isValidJVMMethodName(name: Name) =
!name.exists(c => c == '.' || c == ';' || c =='[' || c == '/' || c == '<' || c == '>')

def printError(str: String)(implicit ctx: Context) = {
ctx.println(Console.RED + "[error] " + Console.WHITE + str)
}
Expand Down Expand Up @@ -130,6 +137,7 @@ class TreeChecker extends Phase with SymTransformer {
def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = tree match {
case tree: DefTree =>
val sym = tree.symbol
assert(isValidJVMName(sym.name), s"${sym.fullName} name is invalid on jvm")
everDefinedSyms.get(sym) match {
case Some(t) =>
if (t ne tree)
Expand Down Expand Up @@ -257,12 +265,24 @@ class TreeChecker extends Phase with SymTransformer {
assert(cls.primaryConstructor == constr.symbol, i"mismatch, primary constructor ${cls.primaryConstructor}, in tree = ${constr.symbol}")
checkOwner(impl)
checkOwner(impl.constr)

def isNonMagicalMethod(x: Symbol) =
x.is(Method) &&
!x.isCompanionMethod &&
!x.isValueClassConvertMethod &&
x != defn.newRefArrayMethod

val symbolsNotDefined = cls.classInfo.decls.toSet.filter(isNonMagicalMethod) -- impl.body.map(_.symbol) - constr.symbol

assert(symbolsNotDefined.isEmpty, i" $cls tree does not define methods: $symbolsNotDefined")

super.typedClassDef(cdef, cls)
}

override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) =
withDefinedSyms(ddef.tparams) {
withDefinedSymss(ddef.vparamss) {
if (!sym.isClassConstructor) assert(isValidJVMMethodName(sym.name), s"${sym.fullName} name is invalid on jvm")
super.typedDefDef(ddef, sym)
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/dotc/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ class tests extends CompilerTest {

@Test def run_all = runFiles(runDir)

@Test def dotty = compileDir(dottyDir, "tools", "-deep" :: "-Ycheck-reentrant" :: allowDeepSubtypes ++ twice) // note the -deep argument
@Test def dotty = compileDir(dottyDir, ".", "-deep" :: "-Ycheck-reentrant" :: allowDeepSubtypes) // note the -deep argument

@Test def dotc_ast = compileDir(dotcDir, "ast")
@Test def dotc_config = compileDir(dotcDir, "config")
Expand Down
6 changes: 6 additions & 0 deletions tests/pending/pos/i743.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
object NonLocalReturn {
def foo(a: List[Int]): Int = {
a.foreach(x => return x)
0
}
}
7 changes: 7 additions & 0 deletions tests/pending/run/StackMap.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
object Test {
var implicitsCache = null

def main(args: Array[String]): Unit = {
implicitsCache = try{null} catch { case ex: Exception => null }
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
exceptions-2.scala:267: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
try { 1 } catch { case e: java.io.IOException => () }
^
nested1:
Innermost finally
Outermost finally
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions tests/run/i744.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
8 changes: 8 additions & 0 deletions tests/run/i744.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
trait A{
private var s = 1
def getS = s
}

object Test extends A {
def main(args: Array[String]): Unit = println(getS)
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion tests/run/value-class-extractor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ object NonNullChar {
@inline final val None = new NonNullChar(0.toChar)
}

final class SomeProduct /*extends Product3[String, Int, List[String]]*/ {
final class SomeProduct extends Product3[String, Int, List[String]] {
def canEqual(x: Any) = x.isInstanceOf[SomeProduct]
def _1 = "abc"
def _2 = 5
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit 07e24e8

Please sign in to comment.