Skip to content

Simplify and harden duplicates checking in exports #14879

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 5 additions & 20 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1072,33 +1072,17 @@ class Namer { typer: Typer =>
/** The forwarders defined by export `exp` */
private def exportForwarders(exp: Export)(using Context): List[tpd.MemberDef] =
val buf = new mutable.ListBuffer[tpd.MemberDef]
val Export(expr, selectors0) = exp
val Export(expr, selectors) = exp
if expr.isEmpty then
report.error(em"Export selector must have prefix and `.`", exp.srcPos)
return Nil

val renamed = mutable.Set[Name]()
val selectors = selectors0 map {
case sel @ ImportSelector(imported, id @ Ident(alias), bound) if alias != nme.WILDCARD =>
def noAliasSelector =
cpy.ImportSelector(sel)(imported, EmptyTree, bound).asInstanceOf[ImportSelector]
if renamed.contains(alias) then
report.error(i"duplicate rename target", id.srcPos)
noAliasSelector
else if alias == imported.name then
report.warning(i"redundant rename in export", id.srcPos)
noAliasSelector
else
renamed += alias
sel
case sel => sel
}

val path = typedAheadExpr(expr, AnySelectionProto)
checkLegalExportPath(path, selectors)
lazy val wildcardBound = importBound(selectors, isGiven = false)
lazy val givenBound = importBound(selectors, isGiven = true)

val targets = mutable.Set[Name]()
def canForward(mbr: SingleDenotation, alias: TermName): CanForward = {
import CanForward.*
val sym = mbr.symbol
Expand All @@ -1108,8 +1092,8 @@ class Namer { typer: Typer =>
Skip
else if cls.derivesFrom(sym.owner) && (sym.owner == cls || !sym.is(Deferred)) then
No(i"is already a member of $cls")
else if alias == mbr.name.toTermName && renamed.contains(alias) then
No(i"clashes with a renamed export")
else if targets.contains(alias) then
No(i"clashes with another export in the same export clause")
else if sym.is(Override) then
sym.allOverriddenSymbols.find(
other => cls.derivesFrom(other.owner) && !other.is(Deferred)
Expand Down Expand Up @@ -1208,6 +1192,7 @@ class Namer { typer: Typer =>
val size = buf.size
val mbrs = List(name, name.toTypeName).flatMap(path.tpe.member(_).alternatives)
mbrs.foreach(addForwarder(alias, _, span))
targets += alias
if buf.size == size then
val reason = mbrs.map(canForward(_, alias)).collect {
case CanForward.No(whyNot) => i"\n$path.$name cannot be exported because it $whyNot"
Expand Down
15 changes: 8 additions & 7 deletions tests/neg/i14818.check
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
-- Error: tests/neg/i14818.scala:9:12 ----------------------------------------------------------------------------------
-- Error: tests/neg/i14818.scala:9:17 ----------------------------------------------------------------------------------
9 | export M.{A, B as A} // error
| ^
| no eligible member A at M
| M.A cannot be exported because it clashes with a renamed export
| ^^^^^^
| no eligible member B at M
| M.B cannot be exported because it clashes with another export in the same export clause
-- [E050] Type Error: tests/neg/i14818.scala:16:10 ---------------------------------------------------------------------
16 | val x = b(1) // error
| ^
| method b in object T3 does not take parameters
|
| longer explanation available when compiling with `-explain`
-- Error: tests/neg/i14818.scala:19:25 ---------------------------------------------------------------------------------
-- Error: tests/neg/i14818.scala:19:22 ---------------------------------------------------------------------------------
19 | export M.{A as C, B as C} // error
| ^
| duplicate rename target
| ^^^^^^
| no eligible member B at M
| M.B cannot be exported because it clashes with another export in the same export clause
5 changes: 5 additions & 0 deletions tests/neg/i14875.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
object M {
type B
}

export M.{B, B} // error