Skip to content

Commit 426bbf7

Browse files
committed
Export: Type checking
1 parent 3f73fad commit 426bbf7

File tree

4 files changed

+113
-3
lines changed

4 files changed

+113
-3
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2256,7 +2256,7 @@ object Parsers {
22562256
}
22572257

22582258
def derived(impExp: Tree, qual: Tree, selectors: List[Tree]) =
2259-
mkTree(impliedOnly, qual, selectors).withSpan(impExp.span)
2259+
mkTree(importImplied, qual, selectors).withSpan(impExp.span)
22602260

22612261
() => {
22622262
val p = path(thisOK = false, handleImport)

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,12 @@ trait Checking {
594594
ctx.error(ex"$cls cannot be instantiated since it${rstatus.msg}", pos)
595595
}
596596

597+
/** Check that `path` is a legal prefix for an import or export clause */
598+
def checkLegalImportPath(path: Tree)(implicit ctx: Context): Unit = {
599+
checkStable(path.tpe, path.sourcePos)
600+
if (!ctx.isAfterTyper) Checking.checkRealizable(path.tpe, path.posd)
601+
}
602+
597603
/** Check that `tp` is a class type.
598604
* Also, if `traitReq` is true, check that `tp` is a trait.
599605
* Also, if `stablePrefixReq` is true and phase is not after RefChecks,

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

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ class Namer { typer: Typer =>
195195

196196
val TypedAhead: Property.Key[tpd.Tree] = new Property.Key
197197
val ExpandedTree: Property.Key[untpd.Tree] = new Property.Key
198+
val ExportForwarders: Property.Key[List[tpd.MemberDef]] = new Property.Key
198199
val SymOfTree: Property.Key[Symbol] = new Property.Key
199200
val Deriver: Property.Key[typer.Deriver] = new Property.Key
200201

@@ -932,6 +933,104 @@ class Namer { typer: Typer =>
932933

933934
def init(): Context = index(params)
934935

936+
/** Add forwarders as required by the export statements in this class */
937+
private def processExports(implicit ctx: Context): Unit = {
938+
939+
/** The forwarders defined by export `exp`.
940+
*/
941+
def exportForwarders(exp: Export): List[tpd.MemberDef] = {
942+
val buf = new mutable.ListBuffer[tpd.MemberDef]
943+
val Export(_, expr, selectors) = exp
944+
val path = typedAheadExpr(expr, AnySelectionProto)
945+
checkLegalImportPath(path)
946+
947+
def needsForwarder(sym: Symbol) =
948+
sym.is(ImplicitOrImplied) == exp.impliedOnly &&
949+
sym.isAccessibleFrom(path.tpe) &&
950+
!sym.isConstructor &&
951+
!cls.derivesFrom(sym.owner)
952+
953+
/** Add a forwarder with name `alias` or its type name equivalent to `mbr`,
954+
* provided `mbr` is accessible and of the right implicit/non-implicit kind.
955+
*/
956+
def addForwarder(alias: TermName, mbr: SingleDenotation, span: Span): Unit =
957+
if (needsForwarder(mbr.symbol)) {
958+
959+
/** The info of a forwarder to type `ref` which has info `info`
960+
*/
961+
def fwdInfo(ref: Type, info: Type): Type = info match {
962+
case _: ClassInfo =>
963+
HKTypeLambda.fromParams(info.typeParams, ref)
964+
case _: TypeBounds =>
965+
ref
966+
case info: HKTypeLambda =>
967+
info.derivedLambdaType(info.paramNames, info.paramInfos,
968+
fwdInfo(ref.appliedTo(info.paramRefs), info.resultType))
969+
case info => // should happen only in error cases
970+
info
971+
}
972+
973+
val forwarder =
974+
if (mbr.isType)
975+
ctx.newSymbol(
976+
cls, alias.toTypeName,
977+
Final,
978+
fwdInfo(path.tpe.select(mbr.symbol), mbr.info),
979+
coord = span)
980+
else
981+
ctx.newSymbol(
982+
cls, alias,
983+
Method | Final | mbr.symbol.flags & ImplicitOrImplied,
984+
mbr.info,
985+
coord = span)
986+
val forwarderDef =
987+
if (forwarder.isType) tpd.TypeDef(forwarder.asType)
988+
else {
989+
import tpd._
990+
val ref = path.select(mbr.symbol.asTerm)
991+
tpd.polyDefDef(forwarder.asTerm, targs => prefss =>
992+
ref.appliedToTypes(targs).appliedToArgss(prefss)
993+
)
994+
}
995+
buf += forwarderDef.withSpan(span)
996+
}
997+
998+
def addForwardersNamed(name: TermName, alias: TermName, span: Span): Unit = {
999+
val mbrs = List(name, name.toTypeName).flatMap(path.tpe.member(_).alternatives)
1000+
if (mbrs.isEmpty)
1001+
ctx.error(i"no accessible member $name at $path", ctx.source.atSpan(span))
1002+
mbrs.foreach(addForwarder(alias, _, span))
1003+
}
1004+
1005+
def addForwardersExcept(seen: List[TermName], span: Span): Unit =
1006+
for (mbr <- path.tpe.allMembers) {
1007+
val alias = mbr.name.toTermName
1008+
if (!seen.contains(alias)) addForwarder(alias, mbr, span)
1009+
}
1010+
1011+
def recur(seen: List[TermName], sels: List[untpd.Tree]): Unit = sels match {
1012+
case (sel @ Ident(nme.WILDCARD)) :: _ =>
1013+
addForwardersExcept(seen, sel.span)
1014+
case (sel @ Ident(name: TermName)) :: rest =>
1015+
addForwardersNamed(name, name, sel.span)
1016+
recur(name :: seen, rest)
1017+
case Thicket((sel @ Ident(fromName: TermName)) :: Ident(toName: TermName) :: Nil) :: rest =>
1018+
if (toName != nme.WILDCARD) addForwardersNamed(fromName, toName, sel.span)
1019+
recur(fromName :: seen, rest)
1020+
case _ =>
1021+
}
1022+
1023+
recur(Nil, selectors)
1024+
val forwarders = buf.toList
1025+
exp.pushAttachment(ExportForwarders, forwarders)
1026+
forwarders
1027+
}
1028+
1029+
val forwarderss =
1030+
for (exp @ Export(_, _, _) <- rest) yield exportForwarders(exp)
1031+
forwarderss.foreach(_.foreach(fwdr => fwdr.symbol.entered))
1032+
}
1033+
9351034
/** The type signature of a ClassDef with given symbol */
9361035
override def completeInCreationContext(denot: SymDenotation): Unit = {
9371036
val parents = impl.parents
@@ -1068,12 +1167,15 @@ class Namer { typer: Typer =>
10681167

10691168
tempInfo.finalize(denot, parentTypes, finalSelfInfo)
10701169

1170+
1171+
10711172
Checking.checkWellFormed(cls)
10721173
if (isDerivedValueClass(cls)) cls.setFlag(Final)
10731174
cls.info = avoidPrivateLeaks(cls, cls.sourcePos)
10741175
cls.baseClasses.foreach(_.invalidateBaseTypeCache()) // we might have looked before and found nothing
10751176
cls.setNoInitsFlags(parentsKind(parents), bodyKind(rest))
10761177
if (cls.isNoInitsClass) cls.primaryConstructor.setFlag(StableRealizable)
1178+
processExports(localCtx)
10771179
}
10781180
}
10791181

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,8 +1780,7 @@ class Typer extends Namer
17801780

17811781
def typedImport(imp: untpd.Import, sym: Symbol)(implicit ctx: Context): Import = track("typedImport") {
17821782
val expr1 = typedExpr(imp.expr, AnySelectionProto)
1783-
checkStable(expr1.tpe, imp.expr.sourcePos)
1784-
if (!ctx.isAfterTyper) checkRealizable(expr1.tpe, imp.expr.posd)
1783+
checkLegalImportPath(expr1)
17851784
assignType(cpy.Import(imp)(imp.importImplied, expr1, imp.selectors), sym)
17861785
}
17871786

@@ -2205,6 +2204,9 @@ class Typer extends Namer
22052204
}
22062205
case Thicket(stats) :: rest =>
22072206
traverse(stats ++ rest)
2207+
case (stat: untpd.Export) :: rest =>
2208+
buf ++= stat.attachment(ExportForwarders)
2209+
traverse(rest)
22082210
case stat :: rest =>
22092211
val stat1 = typed(stat)(ctx.exprContext(stat, exprOwner))
22102212
checkStatementPurity(stat1)(stat, exprOwner)

0 commit comments

Comments
 (0)