Skip to content

Various fixes for implicit/erased function types #4128

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 4 commits into from
Mar 17, 2018
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
37 changes: 27 additions & 10 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ Standard-Section: "ASTs" TopLevelStat*
BYNAMEtype underlying_Type
PARAMtype Length binder_ASTref paramNum_Nat
POLYtype Length result_Type NamesTypes
METHODtype Length result_Type NamesTypes // needed for refinements
methodType(_, _) Length result_Type NamesTypes // needed for refinements
TYPELAMBDAtype Length result_Type NamesTypes // variance encoded in front of name: +/-/(nothing)
SHAREDtype type_ASTRef
NamesTypes = NameType*
Expand Down Expand Up @@ -226,7 +226,7 @@ Standard Section: "Positions" Assoc*
object TastyFormat {

final val header = Array(0x5C, 0xA1, 0xAB, 0x1F)
val MajorVersion = 4
val MajorVersion = 5
val MinorVersion = 0

/** Tags used to serialize names */
Expand Down Expand Up @@ -392,14 +392,28 @@ object TastyFormat {
final val ANDtpt = 165
final val ORtype = 166
final val ORtpt = 167
final val METHODtype = 168
final val POLYtype = 169
final val TYPELAMBDAtype = 170
final val LAMBDAtpt = 171
final val PARAMtype = 172
final val ANNOTATION = 173
final val TERMREFin = 174
final val TYPEREFin = 175
final val POLYtype = 168
final val TYPELAMBDAtype = 169
final val LAMBDAtpt = 170
final val PARAMtype = 171
final val ANNOTATION = 172
final val TERMREFin = 173
final val TYPEREFin = 174

// In binary: 101100EI
// I = implicit method type
// E = erased method type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test case? I did not see a test where a refinement had implicit or erased parameters. In fact, I am not sure we even want to support this. If we don't support it, we won't need to complicate the TASTY format. Compared to the rest of the format, which is rather lean, this does stick out like a sore thumb.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/dotty-staging/dotty/blob/f66660a590cb488a4ea817dbcd0a5f8f49d68df9/tests/pos/implicit-dep.scala is a testcase for an implicit dependent function type, which ends up being desugared to a refinement with an implicit method

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Seems we have to support this then!

final val METHODtype = 176
final val IMPLICITMETHODtype = 177
final val ERASEDMETHODtype = 178
final val ERASEDIMPLICITMETHODtype = 179

def methodType(isImplicit: Boolean = false, isErased: Boolean = false) = {
val implicitOffset = if (isImplicit) 1 else 0
val erasedOffset = if (isErased) 2 else 0
METHODtype + implicitOffset + erasedOffset
}

final val HOLE = 255

final val firstSimpleTreeTag = UNITconst
Expand Down Expand Up @@ -588,6 +602,9 @@ object TastyFormat {
case BYNAMEtpt => "BYNAMEtpt"
case POLYtype => "POLYtype"
case METHODtype => "METHODtype"
case IMPLICITMETHODtype => "IMPLICITMETHODtype"
case ERASEDMETHODtype => "ERASEDMETHODtype"
case ERASEDIMPLICITMETHODtype => "ERASEDIMPLICITMETHODtype"
case TYPELAMBDAtype => "TYPELAMBDAtype"
case LAMBDAtpt => "LAMBDAtpt"
case PARAMtype => "PARAMtype"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) {
}
else if (tag >= firstNatASTTreeTag) {
tag match {
case IDENT | IDENTtpt | SELECT | TERMREF | TYPEREF | SELFDEF => printName()
case IDENT | IDENTtpt | SELECT | SELECTtpt | TERMREF | TYPEREF | SELFDEF => printName()
case _ => printNat()
}
printTree()
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ class TreePickler(pickler: TastyPickler) {
case tpe: PolyType if richTypes =>
pickleMethodic(POLYtype, tpe)
case tpe: MethodType if richTypes =>
pickleMethodic(METHODtype, tpe)
pickleMethodic(methodType(isImplicit = tpe.isImplicitMethod, isErased = tpe.isErasedMethod), tpe)
case tpe: ParamRef =>
assert(pickleParamRef(tpe), s"orphan parameter reference: $tpe")
case tpe: LazyRef =>
Expand Down
6 changes: 6 additions & 0 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ class TreeUnpickler(reader: TastyReader,
readMethodic(PolyType, _.toTypeName)
case METHODtype =>
readMethodic(MethodType, _.toTermName)
case IMPLICITMETHODtype =>
readMethodic(ImplicitMethodType, _.toTermName)
case ERASEDMETHODtype =>
readMethodic(ErasedMethodType, _.toTermName)
case ERASEDIMPLICITMETHODtype =>
readMethodic(ErasedImplicitMethodType, _.toTermName)
case TYPELAMBDAtype =>
readMethodic(HKTypeLambda, _.toTypeName)
case PARAMtype =>
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -759,8 +759,8 @@ object Parsers {
case Ident(name) if name != tpnme.WILDCARD && in.token == COLON =>
isValParamList = true
funArgTypesRest(
typedFunParam(paramStart, name.toTermName),
() => typedFunParam(in.offset, ident()))
typedFunParam(paramStart, name.toTermName, imods),
() => typedFunParam(in.offset, ident(), imods))
case t =>
funArgTypesRest(t, funArgType)
}
Expand Down Expand Up @@ -800,9 +800,9 @@ object Parsers {
}

/** TypedFunParam ::= id ':' Type */
def typedFunParam(start: Offset, name: TermName): Tree = atPos(start) {
def typedFunParam(start: Offset, name: TermName, mods: Modifiers = EmptyModifiers): Tree = atPos(start) {
accept(COLON)
makeParameter(name, typ(), Modifiers(Param))
makeParameter(name, typ(), mods | Param)
}

/** InfixType ::= RefinedType {id [nl] refinedType}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,7 @@ class Typer extends Namer
completeParams(params)
val params1 = params.map(typedExpr(_).asInstanceOf[ValDef])
val resultTpt = typed(body)
val companion = if (isImplicit) ImplicitMethodType else MethodType
val companion = MethodType.maker(isImplicit = isImplicit, isErased = isErased)
val mt = companion.fromSymbols(params1.map(_.symbol), resultTpt.tpe)
if (mt.isParamDependent)
ctx.error(i"$mt is an illegal function type because it has inter-parameter dependencies", tree.pos)
Expand Down
3 changes: 3 additions & 0 deletions compiler/test/dotty/tools/dotc/FromTastyTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class FromTastyTests extends ParallelTesting {
// Type miss match after unpickling
"hklub0.scala",

// Closure type miss match
"i4125.scala",

// Missing position
"t1203a.scala",
"t2260.scala",
Expand Down
4 changes: 4 additions & 0 deletions tests/pos/i4125.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
object Test {
def foo: (erased (x: Int, y: Int) => Int) = erased (x, y) => 1
def bar: (erased implicit (x: Int, y: Int) => Int) = erased implicit (x, y) => 1
}
9 changes: 9 additions & 0 deletions tests/pos/implicit-dep.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
trait HasT {
type T
}

object Test {


def foo: implicit Int => implicit (g: HasT) => g.T = ???
}