Skip to content

Commit

Permalink
surface (fix): Fix opaque type reference for Scala 3.3.4 (#3669)
Browse files Browse the repository at this point in the history
Resolves issues when upgrading to Scala 3.3.4
- Surface for nested opaque types
- Simplified a path-dependent type test, as path-dependent type is not
frequently used, it should not be a blocker to use later versions of
Scala. #3533

---------

Co-authored-by: xerial-bot <leo+bot@xerial.org>
  • Loading branch information
xerial and xerial-bot authored Sep 29, 2024
1 parent 5f0e06a commit 525c905
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class PathDependentTypeTest extends AirSpec {

val d = Design.newSilentDesign
.bind[JdbcProfile#Backend#Database].toInstance(
new PathDependentType.MyBackend.DatabaseDef().asInstanceOf[JdbcProfile#Backend#Database]
new PathDependentType.DatabaseDef().asInstanceOf[JdbcProfile#Backend#Database]
)

d.build[JdbcService] { s => s.p.hello shouldBe "hello jdbc" }
Expand All @@ -43,8 +43,9 @@ object PathDependentType {

trait JdbcBackend {
type Database = DatabaseDef
class DatabaseDef {
def hello = "hello jdbc"
}
}

class DatabaseDef {
def hello = "hello jdbc"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,15 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q):
'{ UnionSurface(${ surfaceOf(t.left) }, ${ surfaceOf(t.right) }) }
}

private def extractSymbol(t: TypeRepr) =
val dealiased = t.dealias
val symbolInOwner = t.typeSymbol.maybeOwner.declarations.find(_.name.toString == t.typeSymbol.name.toString)
symbolInOwner.map(_.tree) match
case Some(TypeDef(_, b: TypeTree)) if t == dealiased =>
// t.dealias does not dealias for path dependent types, so extracting the dealiased type from AST.
surfaceOf(b.tpe)
private def extractSymbol(t: TypeRepr): Expr[Surface] =
// t.dealias may not dealias the refernced type for path dependent types,
// so extracting the dealiased type from the type definition in the owner
val symbolInOwner = t.typeSymbol.maybeOwner.declarations.find(_.name == t.typeSymbol.name)
symbolInOwner.map(_.typeRef.dealias) match
case Some(tpe) =>
surfaceOf(tpe)
case _ =>
val dealiased = t.dealias
if t != dealiased then surfaceOf(dealiased)
else surfaceOf(t.simplified)

Expand All @@ -189,9 +190,9 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q):
val inner = extractSymbol(t)
val name = Expr(alias.name)
val fullName = Expr(fullTypeNameOf(t))
'{ Alias(${ name }, ${ fullName }, ${ inner }) }
val expr = '{ Alias(${ name }, ${ fullName }, ${ inner }) }
expr
case t if t.typeSymbol.isType && t.typeSymbol.isAliasType && !belongsToScalaDefault(t) =>
// println(s"=== alias factory: ${t}, ${dealiased}, ${t.simplified}")
val inner = extractSymbol(t)
val s = t.typeSymbol
val name = Expr(s.name)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package wvlet.airframe.surface

import wvlet.airspec.AirSpec

object PathDependentType:
trait MyProfile:
type Backend = MyBackend

trait MyBackend:
type Database = DatabaseDef

class DatabaseDef:
def hello = "hello my"

class PathDependentTypeTest extends AirSpec:
import PathDependentType.*

test("pass dependent types") {
val s = Surface.of[MyProfile#Backend]
s.name shouldBe "Backend"
s.toString shouldBe "Backend:=MyBackend"
}

test("nested path dependent types") {
val s = Surface.of[MyProfile#Backend#Database]
s.name shouldBe "Database"
s.toString shouldBe "Database:=DatabaseDef"
}
5 changes: 2 additions & 3 deletions airspec/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Global / onChangedBuildSource := ReloadOnSourceChanges

val SCALA_2_12 = "2.12.20"
val SCALA_2_13 = "2.13.15"
val SCALA_3 = "3.3.3"
val SCALA_3 = "3.3.4"
val targetScalaVersions = SCALA_3 :: SCALA_2_13 :: SCALA_2_12 :: Nil

val SCALACHECK_VERSION = "1.18.1"
Expand Down Expand Up @@ -130,8 +130,7 @@ def excludePomDependency(excludes: Seq[String]) = { node: XmlNode =>
}).transform(node).head
}

/**
* AirSpec build definitions.
/** AirSpec build definitions.
*
* To make AirSpec a standalone library without any cyclic project references, AirSpec embeds the source code of
* airframe-log, di, surface, etc.
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import xerial.sbt.pack.PackPlugin.{projectSettings, publishPackArchiveTgz}

val SCALA_2_12 = "2.12.20"
val SCALA_2_13 = "2.13.15"
val SCALA_3 = "3.3.3"
val SCALA_3 = "3.3.4"
val uptoScala2 = SCALA_2_13 :: SCALA_2_12 :: Nil
val targetScalaVersions = SCALA_3 :: uptoScala2

Expand Down

0 comments on commit 525c905

Please sign in to comment.