From 3b0afb39e5c1018b3c859d76c554deb527690f16 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 21 Nov 2019 18:14:11 +0100 Subject: [PATCH 01/10] Fix #7264: Propagate prototypes in quoted type patterns --- .../dotty/tools/dotc/typer/QuotesAndSplices.scala | 14 ++++++++++---- compiler/src/dotty/tools/dotc/typer/Typer.scala | 4 ++-- tests/pos/i7264.scala | 6 ++++++ 3 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 tests/pos/i7264.scala diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index aa6cfc2df507..90f5eb2aeda4 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -98,7 +98,7 @@ trait QuotesAndSplices { case _ => } if (ctx.mode.is(Mode.QuotedPattern) && level == 1) - if (isFullyDefined(pt, ForceDegree.all)) { + if (false && isFullyDefined(pt, ForceDegree.all)) { ctx.error(i"Spliced type pattern must not be fully defined. Consider using $pt directly", tree.expr.sourcePos) tree.withType(UnspecifiedErrorType) } @@ -111,7 +111,10 @@ trait QuotesAndSplices { ctx.error("expected a name binding", expr.sourcePos) "$error".toTypeName } - val typeSym = ctx.newSymbol(spliceOwner(ctx), name, EmptyFlags, TypeBounds.empty, NoSymbol, tree.expr.span) + val typeSymInfo = pt match + case pt: TypeBounds => pt + case _ => TypeBounds.empty + val typeSym = ctx.newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.expr.span) typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuoted_patternBindHoleAnnot.typeRef)).withSpan(tree.expr.span))) val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))( spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) @@ -317,13 +320,16 @@ trait QuotesAndSplices { ctx.error(missingArgMsg(qctx, defn.QuoteContextClass.typeRef, ""), ctx.source.atSpan(tree.span)) val quoted = tree.quoted - val exprPt = pt.baseType(defn.QuotedExprClass) + val exprPt = pt.baseType(if quoted.isType then defn.QuotedTypeClass else defn.QuotedExprClass) val quotedPt = exprPt.argInfos.headOption match { case Some(argPt: ValueType) => argPt // excludes TypeBounds case _ => defn.AnyType } val quoted0 = desugar.quotedPattern(quoted, untpd.TypedSplice(TypeTree(quotedPt))) - val quoted1 = typedExpr(quoted0, WildcardType)(quoteContext.addMode(Mode.QuotedPattern)) + val quoteCtx = quoteContext.addMode(Mode.QuotedPattern) + val quoted1 = + if quoted.isType then typedType(quoted0, quotedPt)(quoteCtx) + else typedExpr(quoted0, quotedPt)(quoteCtx) val (typeBindings, shape, splices) = splitQuotePattern(quoted1) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b8e262d2d239..918e6972f04d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1389,7 +1389,7 @@ class Typer extends Namer def typedArg(arg: untpd.Tree, tparam: ParamInfo) = { def tparamBounds = tparam.paramInfoAsSeenFrom(tpt1.tpe.appliedTo(tparams.map(_ => TypeBounds.empty))) val (desugaredArg, argPt) = - if (ctx.mode is Mode.Pattern) + if ctx.mode.is(Mode.Pattern) || ctx.mode.is(Mode.QuotedPattern) then (if (untpd.isVarPattern(arg)) desugar.patternVar(arg) else arg, tparamBounds) else (arg, WildcardType) @@ -2177,7 +2177,7 @@ class Typer extends Namer /** Typecheck and adapt tree, returning a typed tree. Parameters as for `typedUnadapted` */ def typed(tree: untpd.Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): Tree = - trace(i"typing $tree", typr, show = true) { + trace(i"typing $tree, pt = $pt", typr, show = true) { record(s"typed $getClass") record("typed total") assertPositioned(tree) diff --git a/tests/pos/i7264.scala b/tests/pos/i7264.scala new file mode 100644 index 000000000000..fa5d60ca356a --- /dev/null +++ b/tests/pos/i7264.scala @@ -0,0 +1,6 @@ +import scala.quoted._ +class Foo { + def f[T2](t: Type[T2])(given QuoteContext) = t match { + case '[ *:[Int, $t] ] => + } +} \ No newline at end of file From f0d85fc8027c383f304ea7c19b8ed989add62bf8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 21 Nov 2019 18:52:01 +0100 Subject: [PATCH 02/10] Test that $t is upper bounded by Tuple --- tests/pos/i7264.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/pos/i7264.scala b/tests/pos/i7264.scala index fa5d60ca356a..1d8181ab50a8 100644 --- a/tests/pos/i7264.scala +++ b/tests/pos/i7264.scala @@ -2,5 +2,6 @@ import scala.quoted._ class Foo { def f[T2](t: Type[T2])(given QuoteContext) = t match { case '[ *:[Int, $t] ] => + '[ *:[Int, $t] ] } } \ No newline at end of file From f14d22c3770934a54bcbd0b81e6b1b0307ec669f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 21 Nov 2019 18:52:33 +0100 Subject: [PATCH 03/10] Revert some changes --- compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 90f5eb2aeda4..d02d23c92dab 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -320,7 +320,7 @@ trait QuotesAndSplices { ctx.error(missingArgMsg(qctx, defn.QuoteContextClass.typeRef, ""), ctx.source.atSpan(tree.span)) val quoted = tree.quoted - val exprPt = pt.baseType(if quoted.isType then defn.QuotedTypeClass else defn.QuotedExprClass) + val exprPt = pt.baseType(defn.QuotedExprClass) val quotedPt = exprPt.argInfos.headOption match { case Some(argPt: ValueType) => argPt // excludes TypeBounds case _ => defn.AnyType diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 918e6972f04d..321cb2cc23bd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1389,7 +1389,7 @@ class Typer extends Namer def typedArg(arg: untpd.Tree, tparam: ParamInfo) = { def tparamBounds = tparam.paramInfoAsSeenFrom(tpt1.tpe.appliedTo(tparams.map(_ => TypeBounds.empty))) val (desugaredArg, argPt) = - if ctx.mode.is(Mode.Pattern) || ctx.mode.is(Mode.QuotedPattern) then + if (ctx.mode is Mode.Pattern) (if (untpd.isVarPattern(arg)) desugar.patternVar(arg) else arg, tparamBounds) else (arg, WildcardType) From f8f47c23dce5b194dab7e5e4356951a940c70ba3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 21 Nov 2019 18:55:13 +0100 Subject: [PATCH 04/10] Revert change of prototype --- compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index d02d23c92dab..083c5c408cbb 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -328,8 +328,8 @@ trait QuotesAndSplices { val quoted0 = desugar.quotedPattern(quoted, untpd.TypedSplice(TypeTree(quotedPt))) val quoteCtx = quoteContext.addMode(Mode.QuotedPattern) val quoted1 = - if quoted.isType then typedType(quoted0, quotedPt)(quoteCtx) - else typedExpr(quoted0, quotedPt)(quoteCtx) + if quoted.isType then typedType(quoted0, WildcardType)(quoteCtx) + else typedExpr(quoted0, WildcardType)(quoteCtx) val (typeBindings, shape, splices) = splitQuotePattern(quoted1) From b2139f6a574ab07dca44ad05bf595df518110e4d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 21 Nov 2019 19:25:15 +0100 Subject: [PATCH 05/10] Remove dead code --- .../tools/dotc/typer/QuotesAndSplices.scala | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 083c5c408cbb..6a58005c213c 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -97,29 +97,25 @@ trait QuotesAndSplices { ctx.warning("Canceled quote directly inside a splice. ${ '[ XYZ ] } is equivalent to XYZ.", tree.sourcePos) case _ => } + if (ctx.mode.is(Mode.QuotedPattern) && level == 1) - if (false && isFullyDefined(pt, ForceDegree.all)) { - ctx.error(i"Spliced type pattern must not be fully defined. Consider using $pt directly", tree.expr.sourcePos) - tree.withType(UnspecifiedErrorType) - } - else { - def spliceOwner(ctx: Context): Symbol = - if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner - val name = tree.expr match { - case Ident(name) => ("$" + name).toTypeName - case expr => - ctx.error("expected a name binding", expr.sourcePos) - "$error".toTypeName - } - val typeSymInfo = pt match - case pt: TypeBounds => pt - case _ => TypeBounds.empty - val typeSym = ctx.newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.expr.span) - typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuoted_patternBindHoleAnnot.typeRef)).withSpan(tree.expr.span))) - val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))( - spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) - pat.select(tpnme.splice) + def spliceOwner(ctx: Context): Symbol = + if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner + val name = tree.expr match { + case Ident(name) => ("$" + name).toTypeName + case expr => + ctx.error("expected a name binding", expr.sourcePos) + "$error".toTypeName } + + val typeSymInfo = pt match + case pt: TypeBounds => pt + case _ => TypeBounds.empty + val typeSym = ctx.newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.expr.span) + typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuoted_patternBindHoleAnnot.typeRef)).withSpan(tree.expr.span))) + val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))( + spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) + pat.select(tpnme.splice) else typedSelect(untpd.Select(tree.expr, tpnme.splice), pt)(spliceContext).withSpan(tree.span) } From 5ef1fec113f99e346e1cbfe63eeabdda384ade8a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 22 Nov 2019 08:54:38 +0100 Subject: [PATCH 06/10] Add extra test --- tests/pos/i7264b.scala | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/pos/i7264b.scala diff --git a/tests/pos/i7264b.scala b/tests/pos/i7264b.scala new file mode 100644 index 000000000000..f401f3076b84 --- /dev/null +++ b/tests/pos/i7264b.scala @@ -0,0 +1,7 @@ +import scala.quoted._ +class Foo { + def f[T2: Type](e: Expr[T2])(given QuoteContext) = e match { + case '{ $x: *:[Int, $t] } => + '[ *:[Int, $t] ] + } +} From d7918d3fce9e865e47c6eebb0a62e67b97fc51b9 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 22 Nov 2019 09:00:57 +0100 Subject: [PATCH 07/10] Add workaround test --- tests/pos/i7264c.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/pos/i7264c.scala diff --git a/tests/pos/i7264c.scala b/tests/pos/i7264c.scala new file mode 100644 index 000000000000..d6abd0113f03 --- /dev/null +++ b/tests/pos/i7264c.scala @@ -0,0 +1,9 @@ +import scala.quoted._ +class Foo { + def f[T2: Type](e: Expr[T2])(given QuoteContext) = e match { + case '{ $x: $t0 } => + t0 match + case '[ *:[Int, $t] ] => + '[ *:[Int, $t] ] + } +} From ddb74bf4e12a7823141f4ec374aba8d14d307e9b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 22 Nov 2019 14:21:33 +0100 Subject: [PATCH 08/10] Propagate type args param bounds proto when in quoted pattern --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 6 ++++-- tests/neg/i7264d.scala | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 tests/neg/i7264d.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 321cb2cc23bd..c6bd512ba42b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1389,8 +1389,10 @@ class Typer extends Namer def typedArg(arg: untpd.Tree, tparam: ParamInfo) = { def tparamBounds = tparam.paramInfoAsSeenFrom(tpt1.tpe.appliedTo(tparams.map(_ => TypeBounds.empty))) val (desugaredArg, argPt) = - if (ctx.mode is Mode.Pattern) + if ctx.mode.is(Mode.Pattern) then (if (untpd.isVarPattern(arg)) desugar.patternVar(arg) else arg, tparamBounds) + else if ctx.mode.is(Mode.QuotedPattern) then + (arg, tparamBounds) else (arg, WildcardType) if (tpt1.symbol.isClass) @@ -3030,7 +3032,7 @@ class Typer extends Namer tree.tpe.EtaExpand(tp.typeParamSymbols) tree.withType(tp1) } - if ((ctx.mode is Mode.Pattern) || tree1.tpe <:< pt) tree1 + if (ctx.mode.is(Mode.Pattern) || ctx.mode.is(Mode.QuotedPattern) || tree1.tpe <:< pt) tree1 else err.typeMismatch(tree1, pt) } diff --git a/tests/neg/i7264d.scala b/tests/neg/i7264d.scala new file mode 100644 index 000000000000..67954e92b422 --- /dev/null +++ b/tests/neg/i7264d.scala @@ -0,0 +1,7 @@ +import scala.quoted._ +class Foo { + def f[T2: Type](e: Expr[T2])(given QuoteContext) = e match { + case '{ $x: *:[Int, Any] } => // error: Type argument Any does not conform to upper bound Tuple + + } +} From 6f04d8eb264867163bd9c9501f915ba587cc1f20 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 2 Dec 2019 13:38:29 +0100 Subject: [PATCH 09/10] Extract base types from prototypes --- compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 6a58005c213c..3d7e0a005709 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -316,7 +316,7 @@ trait QuotesAndSplices { ctx.error(missingArgMsg(qctx, defn.QuoteContextClass.typeRef, ""), ctx.source.atSpan(tree.span)) val quoted = tree.quoted - val exprPt = pt.baseType(defn.QuotedExprClass) + val exprPt = pt.baseType(if quoted.isType then defn.QuotedTypeClass else defn.QuotedExprClass) val quotedPt = exprPt.argInfos.headOption match { case Some(argPt: ValueType) => argPt // excludes TypeBounds case _ => defn.AnyType From de3fa6afb71eaf44152157b009494bf0b19d3f80 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 5 Dec 2019 13:28:21 +0100 Subject: [PATCH 10/10] Fix #7603 again --- .../dotty/tools/dotc/typer/QuotesAndSplices.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 3d7e0a005709..b928b2963857 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -190,11 +190,13 @@ trait QuotesAndSplices { patBuf += pat1 } case Select(pat, _) if tree.symbol == defn.QuotedType_splice => - val sym = tree.tpe.dealias.typeSymbol.asType - val tdef = TypeDef(sym).withSpan(sym.span) - freshTypeBindingsBuff += transformTypeBindingTypeDef(tdef, freshTypePatBuf) - TypeTree(tree.tpe.dealias).withSpan(tree.span) - + val sym = tree.tpe.dealias.typeSymbol + if sym.exists then + val tdef = TypeDef(sym.asType).withSpan(sym.span) + freshTypeBindingsBuff += transformTypeBindingTypeDef(tdef, freshTypePatBuf) + TypeTree(tree.tpe.dealias).withSpan(tree.span) + else + tree case ddef: ValOrDefDef => if (ddef.symbol.hasAnnotation(defn.InternalQuoted_patternBindHoleAnnot)) { val bindingType = ddef.symbol.info match {