Skip to content

Allow "new with" #7831

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 3 commits into from
Jan 7, 2020
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
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2176,9 +2176,9 @@ object Parsers {
in.endMarkerScope(NEW) {
val start = in.skipToken()
def reposition(t: Tree) = t.withSpan(Span(start, in.lastOffset))
possibleBracesStart()
possibleTemplateStart()
val parents =
if in.token == LBRACE then Nil
if in.isNestedStart then Nil
else constrApps(commaOK = false, templateCanFollow = true)
colonAtEOLOpt()
possibleTemplateStart(isNew = true)
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ object ErrorReporting {
if (tree.tpe.widen.exists)
i"${exprStr(tree)} does not take ${kind}parameters"
else {
if (ctx.settings.Ydebug.value) new FatalError("").printStackTrace()
i"undefined: $tree # ${tree.uniqueId}: ${tree.tpe.toString} at ${ctx.phase}"
}

Expand Down
12 changes: 8 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Nullables.scala
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,8 @@ object Nullables with
*/
def postProcessByNameArgs(fn: TermRef, app: Tree)(given ctx: Context): Tree =
fn.widen match
case mt: MethodType if mt.paramInfos.exists(_.isInstanceOf[ExprType]) =>
case mt: MethodType
if mt.paramInfos.exists(_.isInstanceOf[ExprType]) && !fn.symbol.is(Inline) =>
app match
case Apply(fn, args) =>
val dropNotNull = new TreeMap with
Expand All @@ -487,10 +488,10 @@ object Nullables with
case _ => super.typedUnadapted(t, pt, locked)

def postProcess(formal: Type, arg: Tree): Tree =
val arg1 = dropNotNull.transform(arg)
val nestedCtx = ctx.fresh.setNewTyperState()
val arg1 = dropNotNull.transform(arg)(given nestedCtx)
if arg1 eq arg then arg
else
val nestedCtx = ctx.fresh.setNewTyperState()
val arg2 = retyper.typed(arg1, formal)(given nestedCtx)
if nestedCtx.reporter.hasErrors || !(arg2.tpe <:< formal) then
ctx.error(em"""This argument was typed using flow assumptions about mutable variables
Expand All @@ -506,7 +507,10 @@ object Nullables with

def recur(formals: List[Type], args: List[Tree]): List[Tree] = (formals, args) match
case (formal :: formalsRest, arg :: argsRest) =>
val arg1 = postProcess(formal.widenExpr.repeatedToSingle, arg)
val arg1 =
if formal.isInstanceOf[ExprType]
then postProcess(formal.widenExpr.repeatedToSingle, arg)
else arg
val argsRest1 = recur(
if formal.isRepeatedParam then formals else formalsRest,
argsRest)
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ trait TypeAssigner {
else
errorType(i"wrong number of arguments at ${ctx.phase.prev} for $fntpe: ${fn.tpe}, expected: ${fntpe.paramInfos.length}, found: ${args.length}", tree.sourcePos)
case t =>
if (ctx.settings.Ydebug.value) new FatalError("").printStackTrace()
errorType(err.takesNoParamsStr(fn, ""), tree.sourcePos)
}
ConstFold(tree.withType(ownType))
Expand Down
8 changes: 8 additions & 0 deletions tests/neg-custom-args/explicit-nulls/byname-nullables1.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- Error: tests/neg-custom-args/explicit-nulls/byname-nullables1.scala:10:6 --------------------------------------------
10 | f(x.fld != null) // error
| ^^^^^^^^^^^^^
| This argument was typed using flow assumptions about mutable variables
| but it is passed to a by-name parameter where such flow assumptions are unsound.
| Wrapping the argument in `byName(...)` fixes the problem by disabling the flow assumptions.
|
| `byName` needs to be imported from the `scala.compiletime` package.
11 changes: 11 additions & 0 deletions tests/neg-custom-args/explicit-nulls/byname-nullables1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
def f(op: => Boolean): Unit = ()
def f(op: Int): Unit = ()

class C with
var fld: String | Null = null

def test() =
var x: C | Null = C()
if x != null then
f(x.fld != null) // error
require(x.fld != null, "error") // ok
File renamed without changes.
109 changes: 109 additions & 0 deletions tests/run/LazyLists.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package xcollections with
import annotation.unchecked.uncheckedVariance

abstract class LazyList[+T] with

private var myHead: T = _
private var myTail: LazyList[T] = _
private var myForced: LazyList[T] | Null = null

protected def force(): LazyList[T]

protected def set(hd: T @uncheckedVariance, tl: LazyList[T] @uncheckedVariance): LazyList[T] =
assert(myForced == null, "implementation error: attempting to re-define existing LazyList")
myHead = hd
myTail = tl
this

def forced(): LazyList[T] =
var myForced = this.myForced
if myForced == null then
myForced = force()
this.myForced = myForced
assert(myForced.myForced != null, "implementation error: LazyList was not forced")
myForced
else myForced

def isEmpty: Boolean = forced() eq LazyList.empty

def head: T =
val e = forced()
require(!e.isEmpty, "head on empty LazyList")
e.myHead

def tail: LazyList[T] =
val e = forced()
require(!e.isEmpty, "tail on empty LazyList")
e.myTail

def fromIterable[T](xs: Iterable[T]) = xs match
case xs: LazyList[T] @unchecked => xs
case _ => LazyList.fromIterator(xs.iterator)

object LazyList with

val empty: LazyList[Nothing] = new with
protected def force(): LazyList[Nothing] = this

object #:: with
def unapply[T](xs: LazyList[T]): Option[(T, LazyList[T])] =
if xs.isEmpty then None
else Some((xs.head, xs.tail))

def fromIterator[T](it: Iterator[T]): LazyList[T] = new with
protected def force() =
if it.hasNext then set(it.next, fromIterator (it))
else empty

given [T, U >: T](xs: LazyList[T]) extended with

def #::(x: U): LazyList[U] = new with
protected def force(): LazyList[U] =
set(x, xs)

def ++(ys: LazyList[U]): LazyList[U] = new with
protected def force() =
if xs.isEmpty then ys.forced()
else set(xs.head, xs.tail ++ ys)

given [T, U](xs: LazyList[T]) extended with
def map(f: T => U): LazyList[U] = new with
protected def force() =
if xs.isEmpty then empty
else set(f(xs.head), xs.tail.map(f))

def flatMap(f: T => LazyList[U]): LazyList[U] = new with
protected def force(): LazyList[U] =
if xs.isEmpty then empty
else f(xs.head) ++ xs.tail.flatMap(f)

def foldLeft(z: U)(f: (U, T) => U): U =
if xs.isEmpty then z
else xs.tail.foldLeft(f(z, xs.head))(f)

given [T](xs: LazyList[T]) extended with
def filter(p: T => Boolean): LazyList[T] = new with
protected def force(): LazyList[T] =
if xs.isEmpty then empty
else if p(xs.head) then set(xs.head, xs.tail.filter(p))
else xs.tail.filter(p)

def take(n: Int): LazyList[T] =
if n <= 0 then empty
else new with
protected def force(): LazyList[T] =
if xs.isEmpty then xs
else set(xs.head, xs.tail.take(n - 1))

def drop(n: Int): LazyList[T] =
if n <= 0 then xs
else new with
protected def force(): LazyList[T] =
def advance(xs: LazyList[T], n: Int): LazyList[T] =
if n <= 0 || xs.isEmpty then xs
else advance(xs.tail, n - 1)
advance(xs, n)
end LazyList
end xcollections

@main def Test() = ()