Skip to content

Commit bd8a6dc

Browse files
committed
Merge pull request #1160 from dotty-staging/add/collection-strawman
Add/collection strawman
2 parents d875fef + 7dfab5f commit bd8a6dc

File tree

6 files changed

+601
-6
lines changed

6 files changed

+601
-6
lines changed

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

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,10 @@ trait Applications extends Compatibility { self: Typer =>
10891089
val alts2 = narrowByShapes(alts1)
10901090
//ctx.log(i"narrowed by shape: ${alts1.map(_.symbol.showDcl)}%, %")
10911091
if (isDetermined(alts2)) alts2
1092-
else narrowByTrees(alts2, pt.typedArgs, resultType)
1092+
else {
1093+
pretypeArgs(alts2, pt)
1094+
narrowByTrees(alts2, pt.typedArgs, resultType)
1095+
}
10931096
}
10941097

10951098
case pt @ PolyProto(targs, pt1) =>
@@ -1122,6 +1125,69 @@ trait Applications extends Compatibility { self: Typer =>
11221125
}
11231126
}
11241127

1128+
/** Try to typecheck any arguments in `pt` that are function values missing a
1129+
* parameter type. The expected type for these arguments is the lub of the
1130+
* corresponding formal parameter types of all alternatives. Type variables
1131+
* in formal parameter types are replaced by wildcards. The result of the
1132+
* typecheck is stored in `pt`, to be retrieved when its `typedArgs` are selected.
1133+
* The benefit of doing this is to allow idioms like this:
1134+
*
1135+
* def map(f: Char => Char): String = ???
1136+
* def map[U](f: Char => U): Seq[U] = ???
1137+
* map(x => x.toUpper)
1138+
*
1139+
* Without `pretypeArgs` we'd get a "missing parameter type" error for `x`.
1140+
* With `pretypeArgs`, we use the union of the two formal parameter types
1141+
* `Char => Char` and `Char => ?` as the expected type of the closure `x => x.toUpper`.
1142+
* That union is `Char => Char`, so we have an expected parameter type `Char`
1143+
* for `x`, and the code typechecks.
1144+
*/
1145+
private def pretypeArgs(alts: List[TermRef], pt: FunProto)(implicit ctx: Context): Unit = {
1146+
def recur(altFormals: List[List[Type]], args: List[untpd.Tree]): Unit = args match {
1147+
case arg :: args1 if !altFormals.exists(_.isEmpty) =>
1148+
def isUnknownParamType(t: untpd.Tree) = t match {
1149+
case ValDef(_, tpt, _) => tpt.isEmpty
1150+
case _ => false
1151+
}
1152+
arg match {
1153+
case arg: untpd.Function if arg.args.exists(isUnknownParamType) =>
1154+
def isUniform[T](xs: List[T])(p: (T, T) => Boolean) = xs.forall(p(_, xs.head))
1155+
val formalsForArg: List[Type] = altFormals.map(_.head)
1156+
// For alternatives alt_1, ..., alt_n, test whether formal types for current argument are of the form
1157+
// (p_1_1, ..., p_m_1) => r_1
1158+
// ...
1159+
// (p_1_n, ..., p_m_n) => r_n
1160+
val decomposedFormalsForArg: List[Option[(List[Type], Type)]] =
1161+
formalsForArg.map(defn.FunctionOf.unapply)
1162+
if (decomposedFormalsForArg.forall(_.isDefined)) {
1163+
val formalParamTypessForArg: List[List[Type]] =
1164+
decomposedFormalsForArg.map(_.get._1)
1165+
if (isUniform(formalParamTypessForArg)((x, y) => x.length == y.length)) {
1166+
val commonParamTypes = formalParamTypessForArg.transpose.map(ps =>
1167+
// Given definitions above, for i = 1,...,m,
1168+
// ps(i) = List(p_i_1, ..., p_i_n) -- i.e. a column
1169+
// If all p_i_k's are the same, assume the type as formal parameter
1170+
// type of the i'th parameter of the closure.
1171+
if (isUniform(ps)(ctx.typeComparer.isSameTypeWhenFrozen(_, _))) ps.head
1172+
else WildcardType)
1173+
val commonFormal = defn.FunctionOf(commonParamTypes, WildcardType)
1174+
overload.println(i"pretype arg $arg with expected type $commonFormal")
1175+
pt.typedArg(arg, commonFormal)
1176+
}
1177+
}
1178+
case _ =>
1179+
}
1180+
recur(altFormals.map(_.tail), args1)
1181+
case _ =>
1182+
}
1183+
def paramTypes(alt: Type): List[Type] = alt match {
1184+
case mt: MethodType => mt.paramTypes
1185+
case mt: PolyType => paramTypes(mt.resultType)
1186+
case _ => Nil
1187+
}
1188+
recur(alts.map(alt => paramTypes(alt.widen)), pt.args)
1189+
}
1190+
11251191
private def harmonizeWith[T <: AnyRef](ts: List[T])(tpe: T => Type, adapt: (T, Type) => T)(implicit ctx: Context): List[T] = {
11261192
def numericClasses(ts: List[T], acc: Set[Symbol]): Set[Symbol] = ts match {
11271193
case t :: ts1 =>

src/dotty/tools/dotc/typer/ProtoTypes.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ object ProtoTypes {
179179
if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this
180180
else new FunProto(args, resultType, typer)
181181

182-
def argsAreTyped: Boolean = myTypedArgs.nonEmpty || args.isEmpty
182+
def argsAreTyped: Boolean = myTypedArgs.size == args.length
183183

184184
/** The typed arguments. This takes any arguments already typed using
185185
* `typedArg` into account.

0 commit comments

Comments
 (0)