Skip to content

Commit

Permalink
Merge pull request #443 from sjrd/scala-3.5
Browse files Browse the repository at this point in the history
Upgrade to Scala 3.5.0 and support its TASTy format.
  • Loading branch information
sjrd authored Oct 2, 2024
2 parents 59811cb + 9c02c87 commit 4fb1fd4
Show file tree
Hide file tree
Showing 18 changed files with 359 additions and 37 deletions.
7 changes: 4 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import sbt.internal.util.ManagedLogger

import org.scalajs.jsenv.nodejs.NodeJSEnv

val usedScalaCompiler = "3.4.0"
val usedScalaCompiler = "3.5.0"
val usedTastyRelease = usedScalaCompiler
val scala2Version = "2.13.14"

Expand Down Expand Up @@ -52,7 +52,7 @@ val strictCompileSettings = Seq(
scalacOptions ++= Seq(
"-Xfatal-warnings",
"-Yexplicit-nulls",
"-Ysafe-init",
"-Wsafe-init",
"-source:future",
),
)
Expand Down Expand Up @@ -129,7 +129,8 @@ lazy val tastyQuery =
)
},

tastyMiMaPreviousArtifacts := mimaPreviousArtifacts.value,
// Temporarily disabled until we have a published version of tasty-query that can handle 3.4.x.
//tastyMiMaPreviousArtifacts := mimaPreviousArtifacts.value,
tastyMiMaConfig ~= { prev =>
import tastymima.intf._
prev
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ final class Definitions private[tastyquery] (
scalaPackage.getPackageDeclOrCreate(termName("compiletime"))
private val scalaCollectionImmutablePackage =
scalaCollectionPackage.getPackageDeclOrCreate(termName("immutable"))
private val scalaQuotedPackage =
scalaPackage.getPackageDeclOrCreate(termName("quoted"))
private val scalaRuntimePackage =
scalaPackage.getPackageDeclOrCreate(termName("runtime"))

Expand Down Expand Up @@ -439,6 +441,8 @@ final class Definitions private[tastyquery] (
lazy val SeqClass = scalaCollectionImmutablePackage.requiredClass("Seq")
lazy val Function0Class = scalaPackage.requiredClass("Function0")

private[tastyquery] lazy val ContextFunction1Class = scalaPackage.requiredClass("ContextFunction1")

def FunctionNClass(n: Int): ClassSymbol =
withRestrictedContext(scalaPackage.findDecl(typeName(s"Function$n")).asClass)

Expand Down Expand Up @@ -482,6 +486,9 @@ final class Definitions private[tastyquery] (

private[tastyquery] lazy val PolyFunctionClass = scalaPackage.optionalClass("PolyFunction")

private[tastyquery] lazy val QuotedExprClass = scalaQuotedPackage.requiredClass("Expr")
private[tastyquery] lazy val QuotesClass = scalaQuotedPackage.requiredClass("Quotes")

private[tastyquery] def isPolyFunctionSub(tpe: Type)(using Context): Boolean =
PolyFunctionClass.exists(cls => tpe.baseType(cls).isDefined)

Expand Down
2 changes: 2 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ private[tastyquery] object Erasure:
preErase(tpe.parent, keepUnit)
case tpe: RecType =>
preErase(tpe.parent, keepUnit)
case tpe: FlexibleType =>
preErase(tpe.nonNullableType, keepUnit)
case _: ByNameType =>
defn.Function0Class.erasure
case tpe: RepeatedType =>
Expand Down
49 changes: 49 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/Printers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ private[tastyquery] object Printers:
print("[")
printCommaSeparatedList(tpe.args)(print(_))
print("]")
case tpe: FlexibleType =>
print(tpe.nonNullableType)
print("?")
case tpe: ByNameType =>
print("=> ")
print(tpe.resultType)
Expand Down Expand Up @@ -445,6 +448,32 @@ private[tastyquery] object Printers:
print(c)
print(">")
printBlock(bindings, expr)(print(_))

case Quote(body, bodyType) =>
print("'[")
print(bodyType)
print("]")
printBlock(Nil, body)(print(_))

case Splice(expr, spliceType) =>
print("$[")
print(spliceType)
print("]")
printBlock(Nil, expr)(print(_))

case SplicePattern(pattern, targs, args, spliceType) =>
print("$[")
print(spliceType)
print("]")
print(pattern)
if targs.nonEmpty then
print("[")
printCommaSeparatedList(targs)(print(_))
print("]")
if args.nonEmpty then
print("(")
printCommaSeparatedList(args)(print(_))
print(")")
end print

def print(caze: CaseDef): Unit =
Expand Down Expand Up @@ -577,6 +606,26 @@ private[tastyquery] object Printers:

case ExprPattern(expr) =>
print(expr)

case QuotePattern(bindings, body, quotes, patternType) =>
print("'<")
print(quotes)
print(">")
body match
case Left(termBody) =>
print("{ ")
for binding <- bindings do
print(binding)
print("; ")
print(termBody)
print(" }")
case Right(typeBody) =>
print("[ ")
for binding <- bindings do
print(binding)
print("; ")
print(typeBody)
print(" ]")
end print

def print(tree: TypeTree): Unit = tree match
Expand Down
6 changes: 6 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/Subtyping.scala
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ private[tastyquery] object Subtyping:
isSubType(tp1, tp2.first) || isSubType(tp1, tp2.second)
|| level4(tp1, tp2)

case tp2: FlexibleType =>
isSubType(tp1, tp2.nullableType)

case tp2: ByNameType =>
tp1 match
case tp1: ByNameType => isSubType(tp1.resultType, tp2.resultType)
Expand Down Expand Up @@ -478,6 +481,9 @@ private[tastyquery] object Subtyping:
case tp1: RecType =>
isSubType(tp1.parent, tp2)

case tp1: FlexibleType =>
isSubType(tp1.nonNullableType, tp2)

case tp1: AndType =>
// TODO Try and simplify first
isSubType(tp1.first, tp2) || isSubType(tp1.second, tp2)
Expand Down
12 changes: 12 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/Traversers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ object Traversers:
traverse(expr)
traverse(caller)
traverse(bindings)
case Quote(body, bodyType) =>
traverse(body)
case Splice(expr, spliceType) =>
traverse(expr)
case SplicePattern(pattern, targs, args, spliceType) =>
traverse(pattern)
traverse(targs)
traverse(args)
case _: ImportIdent | _: Ident | _: This | _: Literal =>
()

Expand All @@ -139,6 +147,10 @@ object Traversers:
traverse(expr)
case WildcardPattern(tpe) =>
()
case QuotePattern(bindings, body, quotes, patternType) =>
traverse(bindings)
body.fold(traverse(_), traverse(_))
traverse(quotes)

// TypeTree-ish
case TypeIdent(tpe) =>
Expand Down
86 changes: 86 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,33 @@ object Trees {
override final def withPos(pos: SourcePosition): ExprPattern = ExprPattern(expr)(pos)
end ExprPattern

/** A tree representing a quote pattern `'{ type binding1; ...; body }` or `'[ type binding1; ...; body ]`.
*
* The `bindings` contain the list of quote pattern type variable definitions (`TypeTreeBind`s)
* in the order in which they are defined in the source.
*
* @param bindings
* Type variable definitions
* @param body
* Quoted pattern (without type variable definitions):
* `Left(termTree)` for a term quote pattern `'{ ... }` or
* `Right(typeTree)` for a type quote pattern `'[ ... ]`
* @param quotes
* A reference to the given `Quotes` instance in scope
* @param patternType
* The type of the pattern
*/
final case class QuotePattern(
bindings: List[TypeTreeBind],
body: Either[TermTree, TypeTree],
quotes: TermTree,
patternType: Type
)(pos: SourcePosition)
extends PatternTree(pos) {
override def withPos(pos: SourcePosition): QuotePattern =
QuotePattern(bindings, body, quotes, patternType)(pos)
}

/** Seq(elems)
* @param tpt The element type of the sequence.
*/
Expand Down Expand Up @@ -717,6 +744,65 @@ object Trees {
override final def withPos(pos: SourcePosition): Inlined = Inlined(expr, caller, bindings)(pos)
}

/** A tree representing a quote `'{ body }`.
*
* @param body
* The tree that was quoted
* @param bodyType
* Explicit type of quoted body, which is the source of truth from which we build the type of the quote
*/
final case class Quote(body: TermTree, bodyType: Type)(pos: SourcePosition) extends TermTree(pos) {
protected final def calculateType(using Context): TermType =
// `Quotes ?=> Expr[bodyType]`
defn.ContextFunction1Class.staticRef.appliedTo(
List(defn.QuotesClass.staticRef, defn.QuotedExprClass.staticRef.appliedTo(bodyType))
)
end calculateType

override final def withPos(pos: SourcePosition): Quote =
Quote(body, bodyType)(pos)
}

/** A tree representing a splice `${ expr }`.
*
* @param expr
* The tree that was spliced
*/
final case class Splice(expr: TermTree, spliceType: Type)(pos: SourcePosition) extends TermTree(pos) {
protected final def calculateType(using Context): TermType =
spliceType

override final def withPos(pos: SourcePosition): Splice =
Splice(expr, spliceType)(pos)
}

/** A tree representing a pattern splice `${ pattern }`, `$ident` or `$ident(args*)` in a quote pattern.
*
* Parser will only create `${ pattern }` and `$ident`, hence they will not have args.
* While typing, the `$ident(args*)` the args are identified and desugared into a `SplicePattern`
* containing them.
*
* `SplicePattern` can only be contained within a `QuotePattern`.
*
* @param pattern
* The pattern that was spliced
* @param targs
* The type arguments of the splice (the HOAS arguments)
* @param args
* The arguments of the splice (the HOAS arguments)
* @param spliceType
* The type of the splice, i.e., of this tree
*/
final case class SplicePattern(pattern: PatternTree, targs: List[TypeTree], args: List[TermTree], spliceType: Type)(
pos: SourcePosition
) extends TermTree(pos) {
protected final def calculateType(using Context): TermType =
spliceType

override final def withPos(pos: SourcePosition): SplicePattern =
SplicePattern(pattern, targs, args, spliceType)(pos)
}

// --- TypeTrees ------------------------------------------------------------

sealed abstract class TypeArgTree(pos: SourcePosition) extends Tree(pos):
Expand Down
5 changes: 5 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/TypeMaps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ private[tastyquery] object TypeMaps {
tp.derivedAnnotatedType(underlying, annot)
protected def derivedMatchType(tp: MatchType, bound: Type, scrutinee: Type, cases: List[MatchTypeCase]): Type =
tp.derivedMatchType(bound, scrutinee, cases)
protected def derivedFlexibleType(tp: FlexibleType, nonNullableType: Type): Type =
tp.derivedFlexibleType(nonNullableType)
protected def derivedByNameType(tp: ByNameType, restpe: Type): Type =
tp.derivedByNameType(restpe)
protected def derivedRepeatedType(tp: RepeatedType, elemType: Type): Type =
Expand Down Expand Up @@ -123,6 +125,9 @@ private[tastyquery] object TypeMaps {
case tp: TypeLambda =>
mapOverLambda(tp)

case tp: FlexibleType =>
derivedFlexibleType(tp, this(tp.nonNullableType))

case tp: ByNameType =>
derivedByNameType(tp, this(tp.resultType))

Expand Down
2 changes: 2 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ private[tastyquery] object TypeOps:
apply(z, tp.thistpe)
case tp: AppliedType =>
tp.args.foldLeft(apply(z, tp.tycon))(this)
case tp: FlexibleType =>
apply(z, tp.nonNullableType)
case tp: ByNameType =>
apply(z, tp.resultType)
case tp: RepeatedType =>
Expand Down
20 changes: 19 additions & 1 deletion tasty-query/shared/src/main/scala/tastyquery/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import tastyquery.Utils.*
* +- AppliedType `C[T1, ..., Tn]`
* +- ByNameType type of a by-name parameter `=> T`
* +- ThisType `C.this`
* +- FlexibleType `U?`
* +- OrType `A | B`
* +- AndType `A & B`
* +- TypeLambda `[T1, ..., Tn] => R`
Expand Down Expand Up @@ -579,7 +580,8 @@ object Types {
self.superType.typeParams
case self: AnnotatedType =>
self.superType.typeParams
case _: SingletonType | _: RefinedType | _: ByNameType | _: RepeatedType | _: MatchType | _: RecType =>
case _: SingletonType | _: RefinedType | _: FlexibleType | _: ByNameType | _: RepeatedType | _: MatchType |
_: RecType =>
// These types are always proper types
Nil
case _: NothingType | _: AnyKindType =>
Expand Down Expand Up @@ -1533,6 +1535,22 @@ object Types {
override def toString(): String = s"AppliedType($tycon, $args)"
}

/** A flexible type `T?`, with `T | Null <: T? <: T`. */
final class FlexibleType(val nonNullableType: Type) extends TypeProxy {
private val myNullableType: Memo[Type] = uninitializedMemo

final def nullableType(using Context): Type = memoized(myNullableType) {
OrType(nonNullableType, defn.NullType)
}

override def underlying(using Context): Type = nonNullableType

private[tastyquery] final def derivedFlexibleType(nonNullableType: Type): FlexibleType =
if nonNullableType eq this.nonNullableType then this else FlexibleType(nonNullableType)

override def toString(): String = s"FlexibleType($nonNullableType)"
}

/** A by-name parameter type of the form `=> T`. */
final class ByNameType(val resultType: Type) extends TypeProxy {
override def underlying(using Context): Type = resultType
Expand Down
Loading

0 comments on commit 4fb1fd4

Please sign in to comment.