Skip to content

Applied types, unoptimized #3061

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 111 commits into from
Sep 21, 2017
Merged

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Sep 5, 2017

This PR covers AppliedTypes before any attempt at optimizations. Recent experience has shown that attempts as optimizations often have unintuitive results, so we should test them very carefully and measure each of them individually.

Copy link
Member

@dottybot dottybot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, and thank you for opening this PR! 🎉

All contributors have signed the CLA, thank you! ❤️

Commit Messages

We want to keep history, but for that to actually be useful we have
some rules on how to format our commit messages (relevant xkcd).

Please stick to these guidelines for commit messages:

  1. Separate subject from body with a blank line
  2. When fixing an issue, start your commit message with Fix #<ISSUE-NBR>:
  3. Limit the subject line to 72 characters
  4. Capitalize the subject line
  5. Do not end the subject line with a period
  6. Use the imperative mood in the subject line ("Added" instead of "Add")
  7. Wrap the body at 80 characters
  8. Use the body to explain what and why vs. how

adapted from https://chris.beams.io/posts/git-commit

Have an awesome day! ☀️

@odersky
Copy link
Contributor Author

odersky commented Sep 5, 2017

test performance please

@dottybot
Copy link
Member

dottybot commented Sep 5, 2017

performance test scheduled: 1 jobs in total.

@dottybot
Copy link
Member

dottybot commented Sep 5, 2017

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/3061 to see the changes.

Benchmarks for this PR is based on the merge of this PR (d92e3e6) with master

yield new HasProblemBounds(mbr)

def baseTypeProblems(base: Type) = base match {
case AndType(base1, base2) =>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complication: Realizablility used to just check that all type members have good bounds. Now it has to look at basetypes as well.

def apply(tp: Type) = mapOver {
tp match {
case tp: RefinedType if param occursIn tp.refinedInfo => tp.parent
case tp @ AppliedType(tycon, args) =>
tp.derivedAppliedType(tycon,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this case was missing before, we'd need one for HKApply anyway.

case _: TypeBounds =>
if (fromBelow) addLess(bound, param) else addLess(param, bound)
case tp =>
if (fromBelow) isSubType(bound, tp) else isSubType(tp, bound)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added case prompted by a failure. But it looks like we'd need it anyway, and we just failed to have a test that exercises this pass before.

@@ -335,7 +334,7 @@ object Contexts {
* from constructor parameters to class parameter accessors.
*/
def superCallContext: Context = {
val locals = newScopeWith(owner.asClass.paramAccessors: _*)
val locals = newScopeWith(owner.typeParams ++ owner.asClass.paramAccessors: _*)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type parameters are no longer accessors, need to be mentioned explicitly.

"uniqueTypeAliases" -> uniqueTypeAliases)
"uniqueAppliedTypes" -> uniqueAppliedTypes,
"uniqueWithFixedSyms" -> uniqueWithFixedSyms,
"uniqueNamedTypes" -> uniqueNamedTypes)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimize for different set of common types

/** A binding for a type parameter of a base class or trait.
*/
final val BaseTypeArg = typeFlag(25, "<basetypearg>")

Copy link
Contributor Author

@odersky odersky Sep 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplification: No more BaseTypeArgs

@@ -38,6 +38,15 @@ abstract class Periods extends DotClass { self: Context =>
}
Period(runId, first, nxTrans)
}

/** Are all base types in the current period guaranteed to be the same as in period `p`? */
def hasSameBaseTypesAs(p: Period) = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored out because we test this now in several places

@@ -84,19 +84,12 @@ object Config {
/** If this flag is set, take the fast path when comparing same-named type-aliases and types */
final val fastPathForRefinedSubtype = true

/** If this flag is set, `TypeOps.normalizeToClassRefs` will insert forwarders
* for type parameters of base classes. This is an optimization, which avoids
* long alias chains. We should not rely on the optimization, though. So changing
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplification: No paramForwarding or suspensions needed

@@ -1382,6 +1379,14 @@ object SymDenotations {
NoSymbol
}

/** The explicitly given self type (self types of modules are assumed to be
* explcitly given here).
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactoring: givenSelfType moved from ClassInfo to ClassDenotation

case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2)
//case tp: TypeProxy => inCache(tp.underlying) // disabled, can re-enable insyead of last two lines for performance testing
//case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2)
case tp: TypeProxy => isCachable(tp.underlying, btrCache)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems we were needlessly pessimistic before and therefore cached less than we could have.

else subcls.denot match {
case cdenot: ClassDenotation =>
if (cdenot.baseClassSet contains symbol) foldGlb(NoType, tp.parents)
case tp @ TypeRef(prefix, _) =>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complication: Compute applied base types instead of simple TypeRefs.

case tp: TypeBounds => throw new AssertionError("no TypeBounds allowed")
case _ => tp
}

/** If `tp` is a TypeBounds instance return its lower bound else return `tp` */
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactoring: Moved from TypeApplications to Types (as loBound, hiBound)

case _ => None
}
}

/** Extractor for type application T[U_1, ..., U_n]. This is the refined type
*
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplification: AppliedTypes are case classes, no extractor needed.

case _ =>
appliedTo(args)
}

/** Turn this type, which is used as an argument for
* type parameter `tparam`, into a TypeBounds RHS
*/
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplification: Everything is now handled by baseType, no additional methods needed.


/** Argument types where existential types in arguments are approximated by their upper bound */
def argTypesHi(implicit ctx: Context) = argInfos mapConserve boundsToHi

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplification: withoutArgs is replaced by typeConstructor

compareLower(tycon2.info.bounds, tyconIsTypeRef = true)
isMatchingApply(tp1) || {
tycon2.info match {
case info2: TypeBounds =>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complication: Need to compare baseclasses of lhs against full rhs if it is an AppliedType

val tparam = tparams.head
val v = tparam.paramVariance

def compareCaptured(arg1: Type, arg2: Type): Boolean = arg1 match {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complication: Need to do capture conversion when comparing an applied type with wildcard arguments.

isSubArg(arg1, arg2) || {
// last effort: try to adapt variances of higher-kinded types if this is sound.
// TODO: Move this to eta-expansion?
val adapted2 = arg2.adaptHkVariances(tparam.paramInfo)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complication: Need to adapt higher-kinded variances when comparing applied types as well as refined types.

@@ -1203,6 +1244,56 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
final def lub(tps: List[Type]): Type =
((defn.NothingType: Type) /: tps)(lub(_,_, canConstrain = false))

def lubArgs(args1: List[Type], args2: List[Type], tparams: List[TypeParamInfo], canConstrain: Boolean = false): List[Type] =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complications: Need added case for lub and glb of applied types, which mimic the simplifications done for refined types.

// Given two refinements `T1 { X = S1 }` and `T2 { X = S2 }` rewrite to
// `T1 & T2 { X B }` where `B` is the conjunction of the bounds of `X` in `T1` and `T2`.
//
// However, if `homogenizeArgs` is set, and both aliases `X = Si` are
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactoring: Moved to glbArgs

tp.derivedRefinedType(simplify(tp.parent, theMap), tp.refinedName, simplify(tp.refinedInfo, theMap))
case tp: TypeAlias =>
tp.derivedTypeAlias(simplify(tp.alias, theMap))
case AndType(l, r) =>
case AndType(l, r) if !ctx.mode.is(Mode.Type) =>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improvement: Don't simplify & and | types given as types

mergeRefinedOrApplied(parent1, parent2), name1, rinfo1 | rinfo2)
case _ => fail
}
case tp1 @ AppliedType(tycon1, args1) =>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complication: need to duplicate merge logic between RefinedTypes and AppliedTypes

}
*/

private def enterArgBinding(formal: Symbol, info: Type, cls: ClassSymbol, decls: Scope) = {
Copy link
Contributor Author

@odersky odersky Sep 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplifications: No more enterArgBinding, normalizeToClassRefs and related operations

@@ -154,14 +149,6 @@ object Types {
false
}

def isInfixType(implicit ctx: Context): Boolean = this match {
case TypeApplications.AppliedType(tycon, args) =>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactoring: Moved back to RefinedPrinter

case tp @ AppliedType(tycon, args) if tycon.typeSymbol.isClass =>
tycon.parents.map(_.subst(tycon.typeSymbol.typeParams, args)) // @!!! cache?
case tp: TypeRef =>
if (tp.info.isInstanceOf[TempClassInfo]) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complication: Handle missing parents due to incomplete denotations here. (But it's better than to do it via suspensions as was done before).


/** The argument corresponding to class type parameter `tparam` as seen from
* prefix `pre`.
*/
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complication: Need specific logic to substitute an actual argument for a formal parameter. This is somewhat more involved than the previous matchParams, which was removed.

odersky and others added 10 commits September 20, 2017 09:47
 - explain why checkRealizable better
 - add test case for bad bounds in basetypes
 - drop dead code
 - documentation fixes
 - fixes to lubArgs/glbArgs
 - eliminate redundant guard
 - eliminate isClassParam method
Ensure that hkResult and typeParams are in sync, except that
typeParams work also for classes.
This allows us to re-instantiate existentials.scala.
There's a scenario where a NamedType is created with an initial symbol
but no denotation yet. If the denotation of that NamedType is first
computed in a subsequent run, the initially given symbol might be stale,
so the symbol should be recomputed from the denotation.
@smarter
Copy link
Member

smarter commented Sep 20, 2017 via email

Copy link
Member

@smarter smarter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, let's get this in! :) But I'd like to see the MajorVersion in TastyFormat.scala bumped. This is good practice and should also avoid weird error messages when updating a dotty git clone and compiling the bootstrapped compiler (if the bootstrapped compiler has been compiled before, incremental compilation will try to read the old tasty sections and get confused).

@odersky
Copy link
Contributor Author

odersky commented Sep 21, 2017

But I'd like to see the MajorVersion in TastyFormat.scala bumped

I'll do that as part of my-follow-on work on NamedTypes.

@liufengyun
Copy link
Contributor

test performance please

The compilation failure of https://github.com/liufengyun/scalaPB on the bench server originates from this PR.

@dottybot
Copy link
Member

performance test scheduled: 1 job(s) in queue, 1 running.

@dottybot
Copy link
Member

performance test failed:

[check /data/workspace/bench/logs/pull-3061-09-25-15.42.out for more information]

smarter added a commit to smarter/dotty-example-project that referenced this pull request Sep 26, 2017
Types in union need to be fully applied, the previous code was wrong but
happened to work before scala/scala3#3061. See
scala/scala3#3161.
@allanrenucci allanrenucci deleted the test-applied-type branch December 14, 2017 19:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants