From 63f88a7a45e6de6ef1c75c28bc4deef959b226cd Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 16 Feb 2023 10:41:19 +0100 Subject: [PATCH] Warn on use of inferred quote type variable bounds This kind of inference is not reliable in general. We can only consider the bounds from type constructor where the type variable is defined but any other constraints are ignored. Therefore it is not possible to properly infer the type bounds of the type variable. The solution is simple, write the bounds explicitly and just check that those bounds conform to the use site of the type variable. --- .../dotty/tools/dotc/typer/QuotesAndSplices.scala | 2 ++ .../fatal-warnings/quote-type-var-with-bounds.check | 12 ++++++++++++ .../fatal-warnings/quote-type-var-with-bounds.scala | 11 +++++++++++ 3 files changed, 25 insertions(+) create mode 100644 tests/neg-custom-args/fatal-warnings/quote-type-var-with-bounds.check create mode 100644 tests/neg-custom-args/fatal-warnings/quote-type-var-with-bounds.scala diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 8473bd168bc5..5a356df16cf8 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -160,6 +160,8 @@ trait QuotesAndSplices { case _ => TypeBounds.empty val typeSym = newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.span) typeSym.addAnnotation(Annotation(New(ref(defn.QuotedRuntimePatterns_patternTypeAnnot.typeRef)).withSpan(tree.span))) + if !(typeSymInfo =:= TypeBounds.empty) then + report.warning(em"Type variable `$tree` has partially inferred bounds$pt.\n\nConsider defining bounds explicitly `'{ $typeSym$pt; ... }`", tree.srcPos) val pat = typedPattern(expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))( using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) pat.select(tpnme.Underlying) diff --git a/tests/neg-custom-args/fatal-warnings/quote-type-var-with-bounds.check b/tests/neg-custom-args/fatal-warnings/quote-type-var-with-bounds.check new file mode 100644 index 000000000000..a75e5c66d1c9 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/quote-type-var-with-bounds.check @@ -0,0 +1,12 @@ +-- Error: tests/neg-custom-args/fatal-warnings/quote-type-var-with-bounds.scala:9:18 ----------------------------------- +9 | case '{ $x: C[t] } => // error + | ^ + | Type variable `t` has partially inferred bounds <: Int. + | + | Consider defining bounds explicitly `'{ type t <: Int; ... }` +-- Error: tests/neg-custom-args/fatal-warnings/quote-type-var-with-bounds.scala:10:18 ---------------------------------- +10 | case '{ $x: D[t] } => // error + | ^ + | Type variable `t` has partially inferred bounds >: Null <: String. + | + | Consider defining bounds explicitly `'{ type t >: Null <: String; ... }` diff --git a/tests/neg-custom-args/fatal-warnings/quote-type-var-with-bounds.scala b/tests/neg-custom-args/fatal-warnings/quote-type-var-with-bounds.scala new file mode 100644 index 000000000000..6b2b70ef8025 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/quote-type-var-with-bounds.scala @@ -0,0 +1,11 @@ +import scala.quoted.* + +class C[T <: Int] +class D[T >: Null <: String] + +def test(e: Expr[Any])(using Quotes) = + e match + case '{ $x: t } => + case '{ $x: C[t] } => // error + case '{ $x: D[t] } => // error + case '{ type t <: Int; $x: C[`t`] } =>