Skip to content

Add/no double bindings #468

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

Merged
merged 5 commits into from
Apr 9, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/ast/TreeTypeMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import dotty.tools.dotc.transform.SymUtils._
* have two substitutons S1 = [outer#1 := outer#3], S2 = [inner#2 := inner#4] where
* hashtags precede symbol ids. If we do S1 first, we get outer#2.inner#3. If we then
* do S2 we get outer#2.inner#4. But that means that the named type outer#2.inner
* gets two different denotations in the same period. Hence, if -YnoDoubleBindings is
* gets two different denotations in the same period. Hence, if -Yno-double-bindings is
* set, we would get a data race assertion error.
*/
final class TreeTypeMap(
Expand Down
41 changes: 39 additions & 2 deletions src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -588,63 +588,97 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
tree
}

/** A select node with the given selector name and a computed type */
def select(name: Name)(implicit ctx: Context): Select =
Select(tree, name)

/** A select node with the given type */
def select(tp: NamedType)(implicit ctx: Context): Select =
untpd.Select(tree, tp.name).withType(tp)

/** A select node that selects the given symbol. Note: Need to make sure this
* is in fact the symbol you would get when you select with the symbol's name,
* otherwise a data race may occur which would be flagged by -Yno-double-bindings.
*/
def select(sym: Symbol)(implicit ctx: Context): Select =
untpd.Select(tree, sym.name).withType(
TermRef.withSigAndDenot(tree.tpe, sym.name.asTermName, sym.signature, sym.denot.asSeenFrom(tree.tpe)))

def selectWithSig(name: Name, sig: Signature)(implicit ctx: Context) =
/** A select node with the given selector name and signature and a computed type */
def selectWithSig(name: Name, sig: Signature)(implicit ctx: Context): Tree =
untpd.SelectWithSig(tree, name, sig)
.withType(TermRef.withSig(tree.tpe, name.asTermName, sig))

/** A select node with selector name and signature taken from `sym`.
* Note: Use this method instead of select(sym) if the referenced symbol
* might be overridden in the type of the qualifier prefix. See note
* on select(sym: Symbol).
*/
def selectWithSig(sym: Symbol)(implicit ctx: Context): Tree =
selectWithSig(sym.name, sym.signature)

/** A unary apply node with given argument: `tree(arg)` */
def appliedTo(arg: Tree)(implicit ctx: Context): Tree =
appliedToArgs(arg :: Nil)

/** An apply node with given arguments: `tree(arg, args0, ..., argsN)` */
def appliedTo(arg: Tree, args: Tree*)(implicit ctx: Context): Tree =
appliedToArgs(arg :: args.toList)

/** An apply node with given argument list `tree(args(0), ..., args(args.length - 1))` */
def appliedToArgs(args: List[Tree])(implicit ctx: Context): Apply =
Apply(tree, args)

/** The current tree applied to given argument lists:
* `tree (argss(0)) ... (argss(argss.length -1))`
*/
def appliedToArgss(argss: List[List[Tree]])(implicit ctx: Context): Tree =
((tree: Tree) /: argss)(Apply(_, _))

/** The current tree applied to (): `tree()` */
def appliedToNone(implicit ctx: Context): Apply = appliedToArgs(Nil)

/** The current tree applied to given type argument: `tree[targ]` */
def appliedToType(targ: Type)(implicit ctx: Context): Tree =
appliedToTypes(targ :: Nil)

/** The current tree applied to given type arguments: `tree[targ0, ..., targN]` */
def appliedToTypes(targs: List[Type])(implicit ctx: Context): Tree =
appliedToTypeTrees(targs map (TypeTree(_)))

/** The current tree applied to given type argument list: `tree[targs(0), ..., targs(targs.length - 1)]` */
def appliedToTypeTrees(targs: List[Tree])(implicit ctx: Context): Tree =
if (targs.isEmpty) tree else TypeApply(tree, targs)

/** Apply to `()` unless tree's widened type is parameterless */
def ensureApplied(implicit ctx: Context): Tree =
if (tree.tpe.widen.isParameterless) tree else tree.appliedToNone

/** `tree.isInstanceOf[tp]` */
def isInstance(tp: Type)(implicit ctx: Context): Tree =
tree.select(defn.Any_isInstanceOf).appliedToType(tp)

/** tree.asInstanceOf[`tp`] */
def asInstance(tp: Type)(implicit ctx: Context): Tree = {
assert(tp.isValueType, i"bad cast: $tree.asInstanceOf[$tp]")
tree.select(defn.Any_asInstanceOf).appliedToType(tp)
}

/** `tree.asInstanceOf[tp]` unless tree's type already conforms to `tp` */
def ensureConforms(tp: Type)(implicit ctx: Context): Tree =
if (tree.tpe <:< tp) tree else asInstance(tp)

/** `this && that`, for boolean trees `this`, `that` */
def and(that: Tree)(implicit ctx: Context): Tree =
tree.select(defn.Boolean_&&).appliedTo(that)

/** `this || that`, for boolean trees `this`, `that` */
def or(that: Tree)(implicit ctx: Context): Tree =
tree.select(defn.Boolean_||).appliedTo(that)

/** The translation of `tree = rhs`.
* This is either the tree as an assignment, to a setter call.
*/
def becomes(rhs: Tree)(implicit ctx: Context): Tree =
if (tree.symbol is Method) {
val setr = tree match {
Expand All @@ -660,20 +694,23 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {

// --- Higher order traversal methods -------------------------------

def foreachSubTree(f: Tree => Unit)(implicit ctx: Context): Unit = { //TODO should go in tpd.
/** Apply `f` to each subtree of this tree */
def foreachSubTree(f: Tree => Unit)(implicit ctx: Context): Unit = {
val traverser = new TreeTraverser {
def traverse(tree: Tree)(implicit ctx: Context) = foldOver(f(tree), tree)
}
traverser.traverse(tree)
}

/** Is there a subtree of this tree that satisfies predicate `p`? */
def existsSubTree(p: Tree => Boolean)(implicit ctx: Context): Boolean = {
val acc = new TreeAccumulator[Boolean] {
def apply(x: Boolean, t: Tree)(implicit ctx: Context) = x || p(t) || foldOver(x, t)
}
acc(false, tree)
}

/** All subtrees of this tree that satisfy predicate `p`. */
def filterSubTrees(f: Tree => Boolean)(implicit ctx: Context): List[Tree] = {
val buf = new mutable.ListBuffer[Tree]
foreachSubTree { tree => if (f(tree)) buf += tree }
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ class ScalaSettings extends Settings.SettingGroup {
val Ytyperdebug = BooleanSetting("-Ytyper-debug", "Trace all type assignments.")
val Ypatmatdebug = BooleanSetting("-Ypatmat-debug", "Trace pattern matching translation.")
val Yexplainlowlevel = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")
val YnoDoubleBindings = BooleanSetting("-YnoDoubleBindings", "Assert no namedtype is bound twice (should be enabled only if program is error-free).")
val YnoDoubleBindings = BooleanSetting("-Yno-double-bindings", "Assert no namedtype is bound twice (should be enabled only if program is error-free).")

val optimise = BooleanSetting("-optimise", "Generates faster bytecode by applying optimisations to the program") withAbbreviation "-optimize"

Expand Down
6 changes: 3 additions & 3 deletions src/dotty/tools/dotc/transform/InterceptedMethods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ class InterceptedMethods extends MiniPhaseTransform { thisTransform =>
poundPoundValue(qual)
} else if (Any_comparisons contains tree.fun.symbol.asTerm) {
if (tree.fun.symbol eq defn.Any_==) {
qual.select(defn.Any_equals).appliedToArgs(tree.args)
qual.selectWithSig(defn.Any_equals).appliedToArgs(tree.args)
} else if (tree.fun.symbol eq defn.Any_!=) {
qual.select(defn.Any_equals).appliedToArgs(tree.args).select(defn.Boolean_!)
qual.selectWithSig(defn.Any_equals).appliedToArgs(tree.args).select(defn.Boolean_!)
} else unknown
} /* else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) {
// todo: this is needed to support value classes
Expand All @@ -130,7 +130,7 @@ class InterceptedMethods extends MiniPhaseTransform { thisTransform =>
// we get a primitive form of _getClass trying to target a boxed value
// so we need replace that method name with Object_getClass to get correct behavior.
// See SI-5568.
qual.select(defn.Any_getClass).appliedToNone
qual.selectWithSig(defn.Any_getClass).appliedToNone
} else {
unknown
}
Expand Down
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/transform/PatternMatcher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
}

// NOTE: checker must be the target of the ==, that's the patmat semantics for ya
def _equals(checker: Tree, binder: Symbol): Tree = checker.select(nme.equals_).appliedTo(ref(binder))
def _equals(checker: Tree, binder: Symbol): Tree =
tpd.applyOverloaded(checker, nme.EQ, List(ref(binder)), List.empty, defn.BooleanType)

// the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly)
def _asInstanceOf(b: Symbol, tp: Type): Tree = ref(b).ensureConforms(tp) // andType here breaks t1048
Expand Down
12 changes: 6 additions & 6 deletions test/dotc/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ class tests extends CompilerTest {
"-pagewidth", "160")

implicit val defaultOptions = noCheckOptions ++ List(
"-Yno-deep-subtypes",
"-Yno-deep-subtypes", "-Yno-double-bindings",
"-Ycheck:tailrec,resolveSuper,mixin,restoreScopes",
"-d", "./out/"
)

val doEmitBytecode = List("-Ystop-before:terminal")
val failedbyName = List("-Ystop-before:collectEntryPoints") // #288
val failedUnderscore = List("-Ystop-before:collectEntryPoints") // #289
val testPickling = List("-Xprint-types", "-Ytest-pickler", "-Ystop-after:pickler")

val failedOther = List("-Ystop-before:collectEntryPoints") // some non-obvious reason. need to look deeper
val twice = List("#runs", "2", "-YnoDoubleBindings")
val twice = List("#runs", "2")
val staleSymbolError: List[String] = List()

val allowDeepSubtypes = defaultOptions diff List("-Yno-deep-subtypes")
val allowDoubleBindings = defaultOptions diff List("-Yno-double-bindings")

val posDir = "./tests/pos/"
val posSpecialDir = "./tests/pos-special/"
Expand Down Expand Up @@ -88,7 +88,7 @@ class tests extends CompilerTest {
@Test def neg_typedapply() = compileFile(negDir, "typedapply", xerrors = 4)
@Test def neg_typedidents() = compileFile(negDir, "typedIdents", xerrors = 2)
@Test def neg_assignments() = compileFile(negDir, "assignments", xerrors = 3)
@Test def neg_typers() = compileFile(negDir, "typers", xerrors = 12)
@Test def neg_typers() = compileFile(negDir, "typers", xerrors = 12)(allowDoubleBindings)
@Test def neg_privates() = compileFile(negDir, "privates", xerrors = 2)
@Test def neg_rootImports = compileFile(negDir, "rootImplicits", xerrors = 2)
@Test def neg_templateParents() = compileFile(negDir, "templateParents", xerrors = 3)
Expand Down Expand Up @@ -130,8 +130,8 @@ class tests extends CompilerTest {
@Test def dotc = compileDir(dotcDir + "tools/dotc", failedOther)(allowDeepSubtypes ++ twice) // see dotc_core
@Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", failedOther ++ twice)
//similar to dotc_core_pickling but for another anon class. Still during firstTransform
@Test def dotc_config = compileDir(dotcDir + "tools/dotc/config", twice)
@Test def dotc_core = compileDir(dotcDir + "tools/dotc/core", failedOther)(allowDeepSubtypes) // !!!twice gives "data race?" error in InterceptedMethods
@Test def dotc_config = compileDir(dotcDir + "tools/dotc/config")
@Test def dotc_core = compileDir(dotcDir + "tools/dotc/core", failedOther)("-Yno-double-bindings" :: allowDeepSubtypes)// twice omitted to make tests run faster
// error: error while loading ConstraintHandling$$anon$1$,
// class file 'target/scala-2.11/dotty_2.11-0.1-SNAPSHOT.jar(dotty/tools/dotc/core/ConstraintHandling$$anon$1.class)'
// has location not matching its contents: contains class $anon
Expand Down