From dac7f7e20d336a268bb5830645f2ffd95e123ce1 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Fri, 25 Oct 2024 10:48:56 +0200 Subject: [PATCH 1/2] fix: Allow `as` as an infix type in non context bound types [Cherry-picked e7221c672030375a0bd2391befc6e1fb4938693e] --- .../dotty/tools/dotc/parsing/Parsers.scala | 22 +++++++++++-------- tests/pos/i21769.scala | 19 ++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 tests/pos/i21769.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 5a3be6505715..b467833b04a8 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -80,6 +80,9 @@ object Parsers { enum IntoOK: case Yes, No, Nested + enum InContextBound: + case Yes, No + type StageKind = Int object StageKind { val None = 0 @@ -1534,7 +1537,8 @@ object Parsers { /** Same as [[typ]], but if this results in a wildcard it emits a syntax error and * returns a tree for type `Any` instead. */ - def toplevelTyp(intoOK: IntoOK = IntoOK.No): Tree = rejectWildcardType(typ(intoOK)) + def toplevelTyp(intoOK: IntoOK = IntoOK.No, inContextBound: InContextBound = InContextBound.No): Tree = + rejectWildcardType(typ(intoOK, inContextBound)) private def getFunction(tree: Tree): Option[Function] = tree match { case Parens(tree1) => getFunction(tree1) @@ -1594,7 +1598,7 @@ object Parsers { * IntoTargetType ::= Type * | FunTypeArgs (‘=>’ | ‘?=>’) IntoType */ - def typ(intoOK: IntoOK = IntoOK.No): Tree = + def typ(intoOK: IntoOK = IntoOK.No, inContextBound: InContextBound = InContextBound.No): Tree = val start = in.offset var imods = Modifiers() val erasedArgs: ListBuffer[Boolean] = ListBuffer() @@ -1743,7 +1747,7 @@ object Parsers { val tuple = atSpan(start): makeTupleOrParens(args.mapConserve(convertToElem)) typeRest: - infixTypeRest: + infixTypeRest(inContextBound): refinedTypeRest: withTypeRest: annotTypeRest: @@ -1772,7 +1776,7 @@ object Parsers { else if isIntoPrefix then PrefixOp(typeIdent(), typ(IntoOK.Nested)) else - typeRest(infixType()) + typeRest(infixType(inContextBound)) end typ private def makeKindProjectorTypeDef(name: TypeName): TypeDef = { @@ -1827,13 +1831,13 @@ object Parsers { /** InfixType ::= RefinedType {id [nl] RefinedType} * | RefinedType `^` // under capture checking */ - def infixType(): Tree = infixTypeRest(refinedType()) + def infixType(inContextBound: InContextBound = InContextBound.No): Tree = infixTypeRest(inContextBound)(refinedType()) - def infixTypeRest(t: Tree, operand: Location => Tree = refinedTypeFn): Tree = + def infixTypeRest(inContextBound: InContextBound = InContextBound.No)(t: Tree, operand: Location => Tree = refinedTypeFn): Tree = infixOps(t, canStartInfixTypeTokens, operand, Location.ElseWhere, ParseKind.Type, isOperator = !followingIsVararg() && !isPureArrow - && !(isIdent(nme.as) && sourceVersion.isAtLeast(`3.6`)) + && !(isIdent(nme.as) && sourceVersion.isAtLeast(`3.6`) && inContextBound == InContextBound.Yes) && nextCanFollowOperator(canStartInfixTypeTokens)) /** RefinedType ::= WithType {[nl] Refinement} [`^` CaptureSet] @@ -2224,7 +2228,7 @@ object Parsers { /** ContextBound ::= Type [`as` id] */ def contextBound(pname: TypeName): Tree = - val t = toplevelTyp() + val t = toplevelTyp(inContextBound = InContextBound.Yes) val ownName = if isIdent(nme.as) && sourceVersion.isAtLeast(`3.6`) then in.nextToken() @@ -4209,7 +4213,7 @@ object Parsers { else constrApp() match case parent: Apply => parent :: moreConstrApps() case parent if in.isIdent && newSyntaxAllowed => - infixTypeRest(parent, _ => annotType1()) :: Nil + infixTypeRest()(parent, _ => annotType1()) :: Nil case parent => parent :: moreConstrApps() // The term parameters and parent references */ diff --git a/tests/pos/i21769.scala b/tests/pos/i21769.scala new file mode 100644 index 000000000000..afb1c66c97fe --- /dev/null +++ b/tests/pos/i21769.scala @@ -0,0 +1,19 @@ + +infix trait as[From, To] + +val conv: (String as Int) = ??? +given instance: (String as Int) = ??? +def test(ev: (String as Int)) = ??? + +class F + +class K extends (F as K) + +class TC1[X] + +def doSth[X: TC1 as tc] = ??? + +class TC2[X]: + type Self = X + +def doSth2[X: {TC1 as tc1, TC2 as tc2}](x: tc2.Self) = ??? From 07583a1e0d791dbd6fd50d05493aff59d1ad71ae Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Wed, 30 Oct 2024 15:58:33 +0100 Subject: [PATCH 2/2] Change inContextBound to a Boolean flag [Cherry-picked ae9cffa3a3c736103ca3bec2742b7fd7c2b930ac] --- .../src/dotty/tools/dotc/parsing/Parsers.scala | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index b467833b04a8..98dd5ff0f3f4 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -80,9 +80,6 @@ object Parsers { enum IntoOK: case Yes, No, Nested - enum InContextBound: - case Yes, No - type StageKind = Int object StageKind { val None = 0 @@ -1537,7 +1534,7 @@ object Parsers { /** Same as [[typ]], but if this results in a wildcard it emits a syntax error and * returns a tree for type `Any` instead. */ - def toplevelTyp(intoOK: IntoOK = IntoOK.No, inContextBound: InContextBound = InContextBound.No): Tree = + def toplevelTyp(intoOK: IntoOK = IntoOK.No, inContextBound: Boolean = false): Tree = rejectWildcardType(typ(intoOK, inContextBound)) private def getFunction(tree: Tree): Option[Function] = tree match { @@ -1598,7 +1595,7 @@ object Parsers { * IntoTargetType ::= Type * | FunTypeArgs (‘=>’ | ‘?=>’) IntoType */ - def typ(intoOK: IntoOK = IntoOK.No, inContextBound: InContextBound = InContextBound.No): Tree = + def typ(intoOK: IntoOK = IntoOK.No, inContextBound: Boolean = false): Tree = val start = in.offset var imods = Modifiers() val erasedArgs: ListBuffer[Boolean] = ListBuffer() @@ -1831,13 +1828,13 @@ object Parsers { /** InfixType ::= RefinedType {id [nl] RefinedType} * | RefinedType `^` // under capture checking */ - def infixType(inContextBound: InContextBound = InContextBound.No): Tree = infixTypeRest(inContextBound)(refinedType()) + def infixType(inContextBound: Boolean = false): Tree = infixTypeRest(inContextBound)(refinedType()) - def infixTypeRest(inContextBound: InContextBound = InContextBound.No)(t: Tree, operand: Location => Tree = refinedTypeFn): Tree = + def infixTypeRest(inContextBound: Boolean = false)(t: Tree, operand: Location => Tree = refinedTypeFn): Tree = infixOps(t, canStartInfixTypeTokens, operand, Location.ElseWhere, ParseKind.Type, isOperator = !followingIsVararg() && !isPureArrow - && !(isIdent(nme.as) && sourceVersion.isAtLeast(`3.6`) && inContextBound == InContextBound.Yes) + && !(isIdent(nme.as) && sourceVersion.isAtLeast(`3.6`) && inContextBound) && nextCanFollowOperator(canStartInfixTypeTokens)) /** RefinedType ::= WithType {[nl] Refinement} [`^` CaptureSet] @@ -2228,7 +2225,7 @@ object Parsers { /** ContextBound ::= Type [`as` id] */ def contextBound(pname: TypeName): Tree = - val t = toplevelTyp(inContextBound = InContextBound.Yes) + val t = toplevelTyp(inContextBound = true) val ownName = if isIdent(nme.as) && sourceVersion.isAtLeast(`3.6`) then in.nextToken()