Skip to content

Commit 3dad816

Browse files
committed
chore: add support for *unqualified selectors*
1 parent 21969a2 commit 3dad816

File tree

6 files changed

+43
-8
lines changed

6 files changed

+43
-8
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2368,6 +2368,7 @@ object Parsers {
23682368
* | `try' Expr [`finally' Expr]
23692369
* | `throw' Expr
23702370
* | `return' [Expr]
2371+
* |  `.` id
23712372
* | ForExpr
23722373
* | [SimpleExpr `.'] id `=' Expr
23732374
* | PrefixOperator SimpleExpr `=' Expr
@@ -2756,6 +2757,7 @@ object Parsers {
27562757
* SimpleExpr1 ::= literal
27572758
* | xmlLiteral
27582759
* | SimpleRef
2760+
* |  `.` id
27592761
* | `(` [ExprsInParens] `)`
27602762
* | SimpleExpr `.` id
27612763
* | SimpleExpr `.` MatchClause
@@ -2800,6 +2802,9 @@ object Parsers {
28002802
case MACRO =>
28012803
val start = in.skipToken()
28022804
MacroTree(simpleExpr(Location.ElseWhere))
2805+
case DOT =>
2806+
accept(DOT)
2807+
selector(EmptyTree)
28032808
case _ =>
28042809
if isLiteral then
28052810
literal()

compiler/src/dotty/tools/dotc/parsing/Tokens.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ object Tokens extends TokensCommon {
227227
| BitSet(QUOTE, NEW)
228228

229229
final val canStartExprTokens3: TokenSet =
230-
canStartInfixExprTokens | BitSet(INDENT, IF, WHILE, FOR, TRY, THROW)
230+
canStartInfixExprTokens | BitSet(INDENT, IF, WHILE, FOR, TRY, THROW, DOT)
231231

232232
final val canStartExprTokens2: TokenSet = canStartExprTokens3 | BitSet(DO)
233233

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,10 +1096,15 @@ trait Applications extends Compatibility {
10961096
if tpt.isType && typedAheadType(tpt).tpe.typeSymbol.typeParams.isEmpty then
10971097
IgnoredProto(pt)
10981098
else
1099-
pt // Don't ignore expected value types of `new` expressions with parameterized type.
1100-
// If we have a `new C()` with expected type `C[T]` we want to use the type to
1101-
// instantiate `C` immediately. This is necessary since `C` might _also_ have using
1102-
// clauses that we want to instantiate with the best available type. See i15664.scala.
1099+
// Don't ignore expected value types of `new` expressions with parameterized type.
1100+
// If we have a `new C()` with expected type `C[T]` we want to use the type to
1101+
// instantiate `C` immediately. This is necessary since `C` might _also_ have using
1102+
// clauses that we want to instantiate with the best available type. See i15664.scala.
1103+
pt
1104+
// When typing the selector of an `Apply` node, and if the selector is `unqualifed` (e.g. `.some(10)`)
1105+
// We preserve the prototype `pt` and keep it in the prototype passed to type the selector.
1106+
// Unqualified selectors relies on the expected type to resolve the qualifier of the selection.
1107+
case Select(EmptyTree, name) => pt
11031108
case _ => IgnoredProto(pt)
11041109
// Do ignore other expected result types, since there might be an implicit conversion
11051110
// on the result. We could drop this if we disallow unrestricted implicit conversions.

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,7 +1037,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
10371037
def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = {
10381038
record("typedSelect")
10391039

1040-
def typeSelectOnTerm(using Context): Tree =
1040+
def typeSelectOnTerm(tree: untpd.Select)(using Context): Tree =
10411041
if ctx.isJava then
10421042
// permitted selection depends on Java context (type or expression).
10431043
// we don't propagate (as a mode) whether a.b.m is a type name; OK since we only see type contexts.
@@ -1111,9 +1111,14 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
11111111
else if (ctx.isJava && tree.name.isTypeName)
11121112
// scala/bug#3120 Java uses the same syntax, A.B, to express selection from the
11131113
// value A and from the type A. We have to try both. (possibly exponential bc of qualifier retyping)
1114-
tryAlternatively(typeSelectOnTerm)(tryJavaSelectOnType)
1114+
tryAlternatively(typeSelectOnTerm(tree))(tryJavaSelectOnType)
1115+
else if tree.qualifier.isEmpty then
1116+
// TODO: Specify and develop the logic to resolve the qualifier here
1117+
val companion = pt.resultType.typeSymbol.companionModule
1118+
val qualified = cpy.Select(tree)(qualifier = untpd.ref(companion), name = tree.name)
1119+
typeSelectOnTerm(qualified)
11151120
else
1116-
typeSelectOnTerm
1121+
typeSelectOnTerm(tree)
11171122

11181123
warnUnnecessaryNN(tree1)
11191124
tree1
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
enum Opt[+T]:
2+
case none extends Opt[Nothing]
3+
case some[T](value: T) extends Opt[T]
4+
5+
object Proxy:
6+
def opt(o: Opt[Int]): Opt[Int] = o
7+
8+
val _: Opt[Int] = .some(10)
9+
val _: Opt[Any] = .none
10+
11+
val _ = Proxy.opt(.some(10))
12+
val _ = Proxy.opt(.none)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class Opt[+T](v: T)
2+
3+
object Opt:
4+
def some[T](b: T): Opt[T] = Opt(b)
5+
val none: Opt[Nothing] = ???
6+
7+
val _: Opt[Int] = .some(10)
8+
val _: Opt[Int] = .none

0 commit comments

Comments
 (0)