Skip to content
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

add type modes to DottyTypeStealer #13576

Merged
merged 1 commit into from
Sep 22, 2021
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
41 changes: 33 additions & 8 deletions compiler/test/dotty/tools/DottyTypeStealer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,58 @@ import dotc.core.Types.Type
* The type signatures will then be printed (singleton types
* are widened.)
*
* @param kind the kind of type we are inspecting [`rhs`, `method`, `class`, `type`]
* @param source top level Scala definitions, e.g. `"class O { type X }"`
* @param typeStrings Scala type signatures, e.g. `"O#X"`
*
* @syntax markdown
*/
@main def printTypes(source: String, typeStrings: String*) = {
val (_, tpes) = DottyTypeStealer.stealType(source, typeStrings*)
tpes.foreach(println)
@main def printTypes(source: String, kind: String, typeStrings: String*) = {
val k = DottyTypeStealer.Kind.lookup(kind)
val (_, tpes) = DottyTypeStealer.stealType(source, k, typeStrings*)
tpes.foreach(t => println(s"$t [${t.getClass}]"))
}

object DottyTypeStealer extends DottyTest {
def stealType(source: String, typeStrings: String*): (Context, List[Type]) = {

enum Kind:
case `rhs`, `method`, `class`, `type`

def format(name: String, arg: String) = this match
case `rhs` => s"val $name: $arg = ???"
case `method` => s"def $name $arg = ???"
case `class` => s"class $name $arg"
case `type` => s"type $name $arg"

object Kind:

def lookup(kind: String): Kind =
values.find(_.productPrefix == kind).getOrElse {
println(s"unknown kind `$kind`, assuming `$rhs`")
rhs
}

end Kind


def stealType(source: String, kind: Kind, typeStrings: String*): (Context, List[Type]) = {
val dummyName = "x_x_x"
val vals = typeStrings.zipWithIndex.map{case (s, x)=> s"val ${dummyName}$x: $s = ???"}.mkString("\n")
val vals = typeStrings.zipWithIndex.map{case (s, x) => kind.format(dummyName + x, s) }.mkString("\n")
val gatheredSource = s" ${source}\n object A$dummyName {$vals}"
var scontext : Context = null
var tp: List[Type] = null
checkCompile("typer", gatheredSource) {
(tree, context) =>
given Context = context
val findValDef: (List[ValDef], tpd.Tree) => List[ValDef] =
val findMemberDef: (List[MemberDef], tpd.Tree) => List[MemberDef] =
(acc , tree) => tree match {
case t: DefDef if t.name.startsWith(dummyName) => t :: acc
case t: ValDef if t.name.startsWith(dummyName) => t :: acc
case t: TypeDef if t.name.startsWith(dummyName) => t :: acc
case _ => acc
}
val d = new DeepFolder[List[ValDef]](findValDef).foldOver(Nil, tree)
tp = d.map(_.tpe.widen).reverse
val d = new DeepFolder[List[MemberDef]](findMemberDef).foldOver(Nil, tree)
tp = d.map(_.symbol.info).reverse
scontext = context
}
(scontext, tp)
Expand Down
39 changes: 28 additions & 11 deletions docs/docs/contributing/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,35 +40,52 @@ can be enabled through the `dotty.tools.dotc.config.Printers` object. Change any
## Inspecting Types with Type Stealer ##

You can inspect types with the main method `dotty.tools.printTypes` from the sbt shell,
passing at least two arguments. The first argument is a string that introduces some
Scala definitions, the following arguments are type signatures, (i.e. the return type
of a definition) that are allowed to reference definitions from the first argument.

The type signatures will then be printed, displaying their internal structure, using
passing at least three arguments:
- The first argument is a string that introduces some
Scala definitions
- The second argument introduces how the the remaining arguments should be interpreted,
comprising of
- `rhs` - the return type of a definition
- `class` - the signature of a class, after its name
- `method` - the signature of a method, after its name
- `type` - the signature of a type, after its name
- The remaining arguments are type signatures, these may reference definitions introduced by the first argument.

Each type signature is then be printed, displaying their internal structure, alongside their class, using
the same representation that can later be used in pattern matching to decompose the type.

Here, we inspect a refinement of a class `Box`:
```bash
$ sbt
> scala3-compiler-bootstrapped/Test/runMain dotty.tools.printTypes "class Box { def x: Any }" "Box { def x: Int }"
RefinedType(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),class Box),x,ExprType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),class Int)))
> scala3-compiler-bootstrapped/Test/runMain dotty.tools.printTypes "class Box { def x: Any }" "rhs" "Box { def x: Int }"
RefinedType(TypeRef(ThisType(TypeRef(NoPrefix, module class <empty>)),class Box), x, ExprType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix, module class <root>)), object scala), class Int))) [class dotty.tools.dotc.core.Types$CachedRefinedType]
```

You can also pass the empty string as the first
You can also pass the empty string as the second
argument, e.g. to inspect a standard library type:
```bash
$ sbt
> scala3-compiler-bootstrapped/Test/runMain dotty.tools.printTypes "" "1 *: EmptyTuple"
AppliedType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),class *:),List(ConstantType(Constant(1)), TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class scala)),object Tuple$package),type EmptyTuple)))
> scala3-compiler-bootstrapped/Test/runMain dotty.tools.printTypes "" "rhs" "1 *: EmptyTuple"
AppliedType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix, module class <root>)), object scala), class *:), List(ConstantType(Constant(1)), TypeRef(TermRef(ThisType(TypeRef(NoPrefix, module class scala)), object Tuple$package), type EmptyTuple)))
```

Here are some other examples you can follow:
- `...printTypes "" class "[T] extends Foo[T] {}"`
- `...printTypes "" method "(x: Int): x.type"`
- `...printTypes "" type "<: Int" "= [T] =>> List[T]"`

If you want to further inspect the types, and not just print them, the object `dotty.tools.DottyTypeStealer` has a
method `stealType`. It takes the same arguments as `printTypes`, but returns both a `Context` containing the
definitions passed, along with the list of types:
```scala
// compiler/test/dotty/tools/DottyTypeStealer.scala
object DottyTypeStealer extends DottyTest {
def stealType(source: String, typeStrings: String*): (Context, List[Type]) = {

enum Kind:
case `rhs`, `method`, `class`, `type`
...

def stealType(kind: Kind, source: String, typeStrings: String*): (Context, List[Type]) = {
...
}
}
Expand Down