Skip to content

Allow given bindings in patterns #7194

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 10 commits into from
Sep 12, 2019
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
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -930,14 +930,18 @@ object desugar {
else tree
}

/** Invent a name for an anonympus given of type or template `impl`. */
def inventGivenName(impl: Tree)(implicit ctx: Context): SimpleName =
avoidIllegalChars(s"${inventName(impl)}_given".toTermName.asSimpleName)

/** The normalized name of `mdef`. This means
* 1. Check that the name does not redefine a Scala core class.
* If it does redefine, issue an error and return a mangled name instead of the original one.
* 2. If the name is missing (this can be the case for instance definitions), invent one instead.
*/
def normalizeName(mdef: MemberDef, impl: Tree)(implicit ctx: Context): Name = {
var name = mdef.name
if (name.isEmpty) name = name.likeSpaced(avoidIllegalChars(s"${inventName(impl)}_given".toTermName.asSimpleName))
if (name.isEmpty) name = name.likeSpaced(inventGivenName(impl))
if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) {
def kind = if (name.isTypeName) "class" else "object"
ctx.error(em"illegal redefinition of standard $kind $name", mdef.sourcePos)
Expand Down
33 changes: 17 additions & 16 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -308,25 +308,12 @@ object Trees {
/** Tree defines a new symbol */
trait DefTree[-T >: Untyped] extends DenotingTree[T] {
type ThisTree[-T >: Untyped] <: DefTree[T]
override def isDef: Boolean = true
def namedType: NamedType = tpe.asInstanceOf[NamedType]
}

/** Tree defines a new symbol and carries modifiers.
* The position of a MemberDef contains only the defined identifier or pattern.
* The envelope of a MemberDef contains the whole definition and has its point
* on the opening keyword (or the next token after that if keyword is missing).
*/
abstract class MemberDef[-T >: Untyped](implicit @constructorOnly src: SourceFile) extends NameTree[T] with DefTree[T] {
type ThisTree[-T >: Untyped] <: MemberDef[T]

private[this] var myMods: untpd.Modifiers = null

private[dotc] def rawMods: untpd.Modifiers =
if (myMods == null) untpd.EmptyModifiers else myMods

def rawComment: Option[Comment] = getAttachment(DocComment)

def withAnnotations(annots: List[untpd.Tree]): ThisTree[Untyped] = withMods(rawMods.withAnnotations(annots))

def withMods(mods: untpd.Modifiers): ThisTree[Untyped] = {
Expand All @@ -338,14 +325,28 @@ object Trees {
def withFlags(flags: FlagSet): ThisTree[Untyped] = withMods(untpd.Modifiers(flags))
def withAddedFlags(flags: FlagSet): ThisTree[Untyped] = withMods(rawMods | flags)

/** Destructively update modifiers. To be used with care. */
def setMods(mods: untpd.Modifiers): Unit = myMods = mods

override def isDef: Boolean = true
def namedType: NamedType = tpe.asInstanceOf[NamedType]
}

/** Tree defines a new symbol and carries modifiers.
* The position of a MemberDef contains only the defined identifier or pattern.
* The envelope of a MemberDef contains the whole definition and has its point
* on the opening keyword (or the next token after that if keyword is missing).
*/
abstract class MemberDef[-T >: Untyped](implicit @constructorOnly src: SourceFile) extends NameTree[T] with DefTree[T] {
type ThisTree[-T >: Untyped] <: MemberDef[T]

def rawComment: Option[Comment] = getAttachment(DocComment)

def setComment(comment: Option[Comment]): this.type = {
comment.map(putAttachment(DocComment, _))
this
}

/** Destructively update modifiers. To be used with care. */
def setMods(mods: untpd.Modifiers): Unit = myMods = mods

/** The position of the name defined by this definition.
* This is a point position if the definition is synthetic, or a range position
* if the definition comes from source.
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {

abstract class ModsDecorator { def mods: Modifiers }

implicit class modsDeco(val mdef: MemberDef)(implicit ctx: Context) {
implicit class modsDeco(val mdef: DefTree)(implicit ctx: Context) {
def mods: Modifiers = mdef.rawMods
}

Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ class Definitions {
@tu lazy val Compiletime_constValue : Symbol = CompiletimePackageObject.requiredMethod("constValue")
@tu lazy val Compiletime_constValueOpt: Symbol = CompiletimePackageObject.requiredMethod("constValueOpt")
@tu lazy val Compiletime_code : Symbol = CompiletimePackageObject.requiredMethod("code")
@tu lazy val Compiletime_summonFrom : Symbol = CompiletimePackageObject.requiredMethod("summonFrom")
@tu lazy val CompiletimeTestingPackageObject: Symbol = ctx.requiredModule("scala.compiletime.testing.package")
@tu lazy val CompiletimeTesting_typeChecks : Symbol = CompiletimeTestingPackageObject.requiredMethod("typeChecks")

Expand Down
35 changes: 27 additions & 8 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Flags._
import Contexts._
import Names._
import NameKinds.WildcardParamName
import NameOps._
import ast.{Positioned, Trees}
import ast.Trees._
import StdNames._
Expand Down Expand Up @@ -2350,16 +2351,34 @@ object Parsers {
if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1() :: patternAlts() }
else Nil

/** Pattern1 ::= Pattern2 [Ascription]
/** Pattern1 ::= Pattern2 [Ascription]
* | ‘given’ PatVar ‘:’ RefinedType
*/
def pattern1(): Tree = {
val p = pattern2()
if (in.token == COLON) {
in.nextToken()
ascription(p, Location.InPattern)
def pattern1(): Tree =
if (in.token == GIVEN) {
val givenMod = atSpan(in.skipToken())(Mod.Given())
atSpan(in.offset) {
in.token match {
case IDENTIFIER | USCORE if in.name.isVariableName =>
val name = in.name
in.nextToken()
accept(COLON)
val typed = ascription(Ident(nme.WILDCARD), Location.InPattern)
Bind(name, typed).withMods(addMod(Modifiers(), givenMod))
case _ =>
syntaxErrorOrIncomplete("pattern variable expected")
errorTermTree
}
}
}
else {
val p = pattern2()
if (in.token == COLON) {
in.nextToken()
ascription(p, Location.InPattern)
}
else p
}
else p
}

/** Pattern2 ::= [id `@'] InfixPattern
*/
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
if (lo eq hi) optText(lo)(" = " ~ _)
else optText(lo)(" >: " ~ _) ~ optText(hi)(" <: " ~ _)
case Bind(name, body) =>
("given ": Text).provided(tree.symbol.is(Implicit) && !homogenizedView) ~ // Used for scala.quoted.Type in quote patterns (not pickled)
("given ": Text).provided(tree.symbol.isOneOf(GivenOrImplicit) && !homogenizedView) ~ // Used for scala.quoted.Type in quote patterns (not pickled)
changePrec(InfixPrec) { toText(name) ~ " @ " ~ toText(body) }
case Alternative(trees) =>
changePrec(OrPrec) { toText(trees, " | ") }
Expand Down Expand Up @@ -699,7 +699,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
}

/** Print modifiers from symbols if tree has type, overriding the untpd behavior. */
implicit def modsDeco(mdef: untpd.MemberDef)(implicit ctx: Context): untpd.ModsDecorator =
implicit def modsDeco(mdef: untpd.DefTree)(implicit ctx: Context): untpd.ModsDecorator =
new untpd.ModsDecorator {
def mods = if (mdef.hasType) Modifiers(mdef.symbol) else mdef.rawMods
}
Expand Down
26 changes: 25 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,31 @@ trait Applications extends Compatibility {
case err: ErrorType => cpy.Apply(tree)(fun1, proto.unforcedTypedArgs).withType(err)
case TryDynamicCallType => typedDynamicApply(tree, pt)
case _ =>
if (originalProto.isDropped) fun1
if originalProto.isDropped then fun1
else if fun1.symbol == defn.Compiletime_summonFrom then
// Special handling of `summonFrom { ... }`.
// We currently cannot use a macro for that since unlike other inline methods
// summonFrom needs to expand lazily. For instance, in
//
// summonFrom {
// case given A[t] =>
// summonFrom
// case given `t` => ...
// }
// }
//
// the second `summonFrom` should expand only once the first `summonFrom` is
// evaluated and `t` is bound. But normal inline expansion does not behave that
// way: arguments to inline function are expanded before the function call.
// To make this work using regular inlining, we'd need a way to annotate
// an inline function that it should expand only if there are no enclosing
// applications of inline functions.
tree.args match {
case (arg @ Match(EmptyTree, cases)) :: Nil =>
typed(untpd.InlineMatch(EmptyTree, cases).withSpan(arg.span), pt)
case _ =>
errorTree(tree, em"argument to summonFrom must be a pattern matching closure")
}
else
tryEither {
simpleApply(fun1, proto)
Expand Down
20 changes: 10 additions & 10 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
/** Register type of leaf node */
private def registerLeaf(tree: Tree): Unit = tree match {
case _: This | _: Ident | _: TypeTree =>
tree.tpe.foreachPart(registerType, stopAtStatic = true)
tree.typeOpt.foreachPart(registerType, stopAtStatic = true)
case _ =>
}

Expand All @@ -433,11 +433,12 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
/** The Inlined node representing the inlined call */
def inlined(sourcePos: SourcePosition): Tree = {

// Special handling of `constValue[T]` and `constValueOpt[T]`
if (callTypeArgs.length == 1)
if (inlinedMethod == defn.Compiletime_constValue) {
val constVal = tryConstValue
if (!constVal.isEmpty) return constVal
ctx.error(i"not a constant type: ${callTypeArgs.head}; cannot take constValue", call.sourcePos)
ctx.error(em"not a constant type: ${callTypeArgs.head}; cannot take constValue", call.sourcePos)
}
else if (inlinedMethod == defn.Compiletime_constValueOpt) {
val constVal = tryConstValue
Expand Down Expand Up @@ -795,9 +796,9 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
/** Reduce an inline match
* @param mtch the match tree
* @param scrutinee the scrutinee expression, assumed to be pure, or
* EmptyTree for a delegate match
* EmptyTree for a summonFrom
* @param scrutType its fully defined type, or
* ImplicitScrutineeTypeRef for a delegate match
* ImplicitScrutineeTypeRef for a summonFrom
* @param typer The current inline typer
* @return optionally, if match can be reduced to a matching case: A pair of
* bindings for all pattern-bound variables and the RHS of the case.
Expand Down Expand Up @@ -1071,11 +1072,10 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {

override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree =
constToLiteral(betaReduce(super.typedApply(tree, pt))) match {
case res: Apply
if res.symbol == defn.InternalQuoted_exprSplice &&
level == 0 &&
call.symbol.is(Macro) &&
!suppressInline =>
case res: Apply if res.symbol == defn.InternalQuoted_exprSplice
&& level == 0
&& call.symbol.is(Macro)
&& !suppressInline =>
expandMacro(res.args.head, tree.span)
case res => res
}
Expand Down Expand Up @@ -1114,7 +1114,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
def patStr(cdef: untpd.CaseDef) = i"case ${cdef.pat}${guardStr(cdef.guard)}"
val msg =
if (tree.selector.isEmpty)
em"""cannot reduce delegate match with
em"""cannot reduce summonFrom with
| patterns : ${tree.cases.map(patStr).mkString("\n ")}"""
else
em"""cannot reduce inline match with
Expand Down
15 changes: 10 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,7 @@ class Typer extends Namer
tree.selector match {
case EmptyTree =>
if (tree.isInline) {
checkInInlineContext("delegate match", tree.posd)
checkInInlineContext("summonFrom", tree.posd)
val cases1 = tree.cases.mapconserve {
case cdef @ CaseDef(pat @ Typed(Ident(nme.WILDCARD), _), _, _) =>
// case _ : T --> case evidence$n : T
Expand Down Expand Up @@ -1478,18 +1478,23 @@ class Typer extends Namer
tpd.cpy.UnApply(body1)(fn, Nil,
typed(untpd.Bind(tree.name, untpd.TypedSplice(arg)).withSpan(tree.span), arg.tpe) :: Nil)
case _ =>
if (tree.name == nme.WILDCARD) body1
var name = tree.name
if (name == nme.WILDCARD && tree.mods.is(Given)) {
val Typed(_, tpt): @unchecked = tree.body
name = desugar.inventGivenName(tpt)
}
if (name == nme.WILDCARD) body1
else {
// for a singleton pattern like `x @ Nil`, `x` should get the type from the scrutinee
// see tests/neg/i3200b.scala and SI-1503
val symTp =
if (body1.tpe.isInstanceOf[TermRef]) pt1
else body1.tpe.underlyingIfRepeated(isJava = false)
val sym = ctx.newPatternBoundSymbol(tree.name, symTp, tree.span)
if (pt == defn.ImplicitScrutineeTypeRef) sym.setFlag(Given)
val sym = ctx.newPatternBoundSymbol(name, symTp, tree.span)
if (pt == defn.ImplicitScrutineeTypeRef || tree.mods.is(Given)) sym.setFlag(Given)
if (ctx.mode.is(Mode.InPatternAlternative))
ctx.error(i"Illegal variable ${sym.name} in pattern alternative", tree.sourcePos)
assignType(cpy.Bind(tree)(tree.name, body1), sym)
assignType(cpy.Bind(tree)(name, body1), sym)
}
}
}
Expand Down
Loading