Skip to content

Regression (?) in softwaremill/tapir - refined type returned in a macro unable to be matched #19458

Closed
@jchyb

Description

@jchyb

Compiler version

3.4.0-RC1-bin-20240115-31f837e-NIGHTLY,
compiles successfully with 3.3.1

Minimized code

main.scala:

import scala.compiletime._
final class Greater[V]
final class Less[V]

// original codebase used non-transparent inlines in the defs below, but I think this would end up passing an abstract type parameter to the macro, so I don't think that should have worked (but transparent inlines should)
transparent inline given validatorForGreater[N: Numeric, NM <: N](using witness: ValueOf[NM]): ValidatorForPredicate[N, Greater[NM]] = ???
transparent inline given validatorForLess[N: Numeric, NM <: N](using witness: ValueOf[NM]): ValidatorForPredicate[N, Less[NM]] = ???
transparent inline def summonValidators[N, A <: Tuple]: List[ValidatorForPredicate[N, Any]] = {
  inline erasedValue[A] match
    case _: EmptyTuple => Nil
    case _: (head *: tail) =>
      summonInline[ValidatorForPredicate[N, head]]
        .asInstanceOf[ValidatorForPredicate[N, Any]] :: summonValidators[N, tail]
}
transparent inline given validatorForAnd[N, Predicates](using mirror: IntersectionTypeMirror[Predicates]): ValidatorForPredicate[N, Predicates] =
  new ValidatorForPredicate[N, Predicates] {
    summonValidators[N, mirror.ElementTypes]
  }

trait ValidatorForPredicate[Value, Predicate]

@main def main() = validatorForAnd[Int, Greater[1] & Less[3]]

macro.scala:

import scala.quoted.Quotes

import scala.annotation.implicitNotFound
import scala.quoted.*
import scala.collection.View.Empty

trait IntersectionTypeMirror[A] {
  type ElementTypes <: Tuple
}

class IntersectionTypeMirrorImpl[A, T <: Tuple] extends IntersectionTypeMirror[A] {
  override type ElementTypes = T
}

object IntersectionTypeMirror {

  transparent inline given derived[A]: IntersectionTypeMirror[A] = ${ derivedImpl[A] }

  private def derivedImpl[A](using Quotes, Type[A]): Expr[IntersectionTypeMirror[A]] = {
    import quotes.reflect.*

    val tplPrependType = TypeRepr.of[? *: ?]
    val tplConcatType = TypeRepr.of[Tuple.Concat]

    def prependTypes(head: TypeRepr, tail: TypeRepr): TypeRepr =
      AppliedType(tplPrependType, List(head, tail))

    def concatTypes(left: TypeRepr, right: TypeRepr): TypeRepr =
      AppliedType(tplConcatType, List(left, right))

    def rec(tpe: TypeRepr): TypeRepr = {
      tpe.dealias match
        case AndType(left, right) => concatTypes(rec(left), rec(right))
        case t                    => prependTypes(t, TypeRepr.of[EmptyTuple])
    }
    val tupled =
      TypeRepr.of[A].dealias match {
        case and: AndType => rec(and).asType.asInstanceOf[Type[Elems]]
        case tpe          => report.errorAndAbort(s"${tpe.show} is not an intersection type")
      }
    type Elems

    given Type[Elems] = tupled

    Apply(
      TypeApply(
        Select.unique(
          New(
            Applied(
              TypeTree.of[IntersectionTypeMirrorImpl],
              List(
                TypeTree.of[A],
                TypeTree.of[Elems]
              )
            )
          ),
          "<init>"
        ),
        List(
          TypeTree.of[A],
          TypeTree.of[Elems]
        )
      ),
      Nil
    ).asExprOf[IntersectionTypeMirror[A]]
  }
}

Output

[error] ./main.scala:22:20
[error] cannot reduce inline match with
[error]  scrutinee:  scala.compiletime.erasedValue[mirror$proxy1.ElementTypes] : mirror$proxy1.ElementTypes
[error]  patterns :  case _:EmptyTuple
[error]              case _:*:[head @ _, tail @ _]
[error] @main def main() = validatorForAnd[Int, Greater[1] & Less[3]]
[error]                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expectation

I think it should compile. The code used in tapir actually uses non-transparent inlines in code that calls transparent inlines, which I don't think should have worked, but did (probably because of the previous match types). But even after correcting that, the code errors with what seems to be an abstract type member.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions