Skip to content
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

Fix example #35

Open
wants to merge 42 commits into
base: typeclass-experiments
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
c6b3b5c
New modularity language import
odersky Jan 7, 2024
8b12c03
A relaxation concerning exported type aliases
odersky Dec 15, 2023
fba554c
Allow class parents to be refined types.
odersky Dec 13, 2023
ebc6626
Allow vals in using clauses of givens
odersky Nov 18, 2023
8a10f96
Introduce tracked class parameters
odersky Nov 18, 2023
11e3c11
Some tweaks to modularity.md
odersky Jan 7, 2024
c1124bc
Make some context bound evidence params tracked
odersky Dec 25, 2023
88d91cb
Make explicit arguments for context bounds an error from 3.5
odersky Dec 23, 2023
e983566
Use the name of the type parameter as context bound evidence
odersky Jan 7, 2024
5285c7c
Allow contecxt bounds with abstract `This` types
odersky Jan 7, 2024
cd83448
Drop restriction against typedefs at level * only
odersky Jan 7, 2024
c1a04cd
Allow types in given definitions to be infix types
odersky Jan 7, 2024
ed0922f
New syntax for given defs
odersky Jan 7, 2024
96d9487
Rename This to Self as instance type of type classes
odersky Jan 7, 2024
f8c07bb
Add `is` type to Predef
odersky Jan 7, 2024
875dcbf
Allow multiple context bounds in `{...}`
odersky Jan 7, 2024
907635a
Allow renamings `as N` in context bounds
odersky Jan 7, 2024
c48d4e5
Implement `deferredSummon`
odersky Jan 7, 2024
bac4db9
Allow context bounds in type declarations
odersky Jan 7, 2024
3b6600c
Tweak position where synthetic evidence parameters are added
odersky Jan 7, 2024
e3f8252
Fix typing of generated context bound refinements
odersky Jan 7, 2024
0b74e02
Hylolib: Switch to member based type classes
odersky Jan 2, 2024
cd83e4e
Hylolib: Use name of bound parameter for context bounds
odersky Jan 2, 2024
9b289a1
Hylolib: Some stylistic tweaks
odersky Jan 2, 2024
ed4782b
Hylolib: Drop usages of `with` in givens
odersky Jan 2, 2024
8efdd8e
Dealias before checking for illegal class parents
odersky Jan 3, 2024
2cb5cd5
Hylolib: Alternative, statically safe Slice design
odersky Jan 3, 2024
bb63cfd
Rename `deferredSummon` to `deferred` and allow it for defs
odersky Jan 7, 2024
3d7ae34
Treat new style given syntax without a rhs or template as concrete
odersky Jan 7, 2024
c5f513d
Fix typing of RefinedTypes with watching parents
odersky Jan 7, 2024
9f453ca
Show that this solves #10929
odersky Jan 7, 2024
49af9d0
Disambiguate more towards new given syntax, show solution for #15840
odersky Jan 7, 2024
caf0be0
Also reduce term projections
odersky Jan 6, 2024
5479ff4
Refine rules when context bound evidence is tracked
odersky Jan 6, 2024
443fe7a
Add a doc page
odersky Jan 8, 2024
2f3fbe7
Do deferred given resolution only at typer phase
odersky Jan 8, 2024
1d77374
Update SemanticDB expects to account for named context bound evidence…
odersky Jan 8, 2024
e3dcdb5
Fix typos
odersky Jan 8, 2024
aae3a5f
Update docs/_docs/reference/experimental/typeclasses.md
odersky Jan 8, 2024
83e18d3
Update syntax changes section in doc page
odersky Jan 9, 2024
e6d5fa9
Update docs/_docs/reference/experimental/typeclasses.md
odersky Jan 11, 2024
2aa5b93
Fix example
sideeffffect Jan 14, 2024
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
203 changes: 130 additions & 73 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree)(implicit @constructorOnly src: SourceFile) extends DefTree
case class ExtMethods(paramss: List[ParamClause], methods: List[Tree])(implicit @constructorOnly src: SourceFile) extends Tree
case class Into(tpt: Tree)(implicit @constructorOnly src: SourceFile) extends Tree
case class ContextBoundTypeTree(tycon: Tree, paramName: TypeName, ownName: TermName)(implicit @constructorOnly src: SourceFile) extends Tree
case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree

case class ImportSelector(imported: Ident, renamed: Tree = EmptyTree, bound: Tree = EmptyTree)(implicit @constructorOnly src: SourceFile) extends Tree {
Expand Down Expand Up @@ -231,6 +232,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {

case class Infix()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Infix)

case class Tracked()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Tracked)

/** Used under pureFunctions to mark impure function types `A => B` in `FunctionWithMods` */
case class Impure()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Impure)
}
Expand Down Expand Up @@ -669,6 +672,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def Into(tree: Tree)(tpt: Tree)(using Context): Tree = tree match
case tree: Into if tpt eq tree.tpt => tree
case _ => finalize(tree, untpd.Into(tpt)(tree.source))
def ContextBoundTypeTree(tree: Tree)(tycon: Tree, paramName: TypeName, ownName: TermName)(using Context): Tree = tree match
case tree: ContextBoundTypeTree if (tycon eq tree.tycon) && paramName == tree.paramName && ownName == tree.ownName => tree
case _ => finalize(tree, untpd.ContextBoundTypeTree(tycon, paramName, ownName)(tree.source))
def ImportSelector(tree: Tree)(imported: Ident, renamed: Tree, bound: Tree)(using Context): Tree = tree match {
case tree: ImportSelector if (imported eq tree.imported) && (renamed eq tree.renamed) && (bound eq tree.bound) => tree
case _ => finalize(tree, untpd.ImportSelector(imported, renamed, bound)(tree.source))
Expand Down Expand Up @@ -736,6 +742,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
cpy.ExtMethods(tree)(transformParamss(paramss), transformSub(methods))
case Into(tpt) =>
cpy.Into(tree)(transform(tpt))
case ContextBoundTypeTree(tycon, paramName, ownName) =>
cpy.ContextBoundTypeTree(tree)(transform(tycon), paramName, ownName)
case ImportSelector(imported, renamed, bound) =>
cpy.ImportSelector(tree)(transformSub(imported), transform(renamed), transform(bound))
case Number(_, _) | TypedSplice(_) =>
Expand Down Expand Up @@ -793,6 +801,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
this(paramss.foldLeft(x)(apply), methods)
case Into(tpt) =>
this(x, tpt)
case ContextBoundTypeTree(tycon, paramName, ownName) =>
this(x, tycon)
case ImportSelector(imported, renamed, bound) =>
this(this(this(x, imported), renamed), bound)
case Number(_, _) =>
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ object Feature:
val pureFunctions = experimental("pureFunctions")
val captureChecking = experimental("captureChecking")
val into = experimental("into")
val modularity = experimental("modularity")

val globalOnlyImports: Set[TermName] = Set(pureFunctions, captureChecking)

Expand Down
12 changes: 8 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ object Flags {
val (AccessorOrSealed @ _, Accessor @ _, Sealed @ _) = newFlags(11, "<accessor>", "sealed")

/** A mutable var, an open class */
val (MutableOrOpen @ __, Mutable @ _, Open @ _) = newFlags(12, "mutable", "open")
val (MutableOrOpen @ _, Mutable @ _, Open @ _) = newFlags(12, "mutable", "open")

/** Symbol is local to current class (i.e. private[this] or protected[this]
* pre: Private or Protected are also set
Expand Down Expand Up @@ -377,6 +377,9 @@ object Flags {
/** Symbol cannot be found as a member during typer */
val (Invisible @ _, _, _) = newFlags(45, "<invisible>")

/** Tracked modifier for class parameter / a class with some tracked parameters */
val (Tracked @ _, _, Dependent @ _) = newFlags(46, "tracked")

// ------------ Flags following this one are not pickled ----------------------------------

/** Symbol is not a member of its owner */
Expand Down Expand Up @@ -452,7 +455,7 @@ object Flags {
CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque | Open

val TermSourceModifierFlags: FlagSet =
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy | Tracked

/** Flags representing modifiers that can appear in trees */
val ModifierFlags: FlagSet =
Expand All @@ -466,7 +469,7 @@ object Flags {
val FromStartFlags: FlagSet = commonFlags(
Module, Package, Deferred, Method, Case, Enum, Param, ParamAccessor,
Scala2SpecialFlags, MutableOrOpen, Opaque, Touched, JavaStatic,
OuterOrCovariant, LabelOrContravariant, CaseAccessor,
OuterOrCovariant, LabelOrContravariant, CaseAccessor, Tracked,
Extension, NonMember, Implicit, Given, Permanent, Synthetic, Exported,
SuperParamAliasOrScala2x, Inline, Macro, ConstructorProxy, Invisible)

Expand All @@ -477,7 +480,7 @@ object Flags {
*/
val AfterLoadFlags: FlagSet = commonFlags(
FromStartFlags, AccessFlags, Final, AccessorOrSealed,
Abstract, LazyOrTrait, SelfName, JavaDefined, JavaAnnotation, Transparent)
Abstract, LazyOrTrait, SelfName, JavaDefined, JavaAnnotation, Transparent, Tracked)

/** A value that's unstable unless complemented with a Stable flag */
val UnstableValueFlags: FlagSet = Mutable | Method
Expand Down Expand Up @@ -562,6 +565,7 @@ object Flags {
val DeferredOrLazyOrMethod: FlagSet = Deferred | Lazy | Method
val DeferredOrTermParamOrAccessor: FlagSet = Deferred | ParamAccessor | TermParam // term symbols without right-hand sides
val DeferredOrTypeParam: FlagSet = Deferred | TypeParam // type symbols without right-hand sides
val DeferredGivenFlags = Deferred | Given | HasDefault
val EnumValue: FlagSet = Enum | StableRealizable // A Scala enum value
val FinalOrInline: FlagSet = Final | Inline
val FinalOrModuleClass: FlagSet = Final | ModuleClass // A module class or a final class
Expand Down
32 changes: 29 additions & 3 deletions compiler/src/dotty/tools/dotc/core/NamerOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package core
import Contexts.*, Symbols.*, Types.*, Flags.*, Scopes.*, Decorators.*, Names.*, NameOps.*
import SymDenotations.{LazyType, SymDenotation}, StdNames.nme
import TypeApplications.EtaExpansion
import collection.mutable

/** Operations that are shared between Namer and TreeUnpickler */
object NamerOps:
Expand All @@ -14,9 +15,34 @@ object NamerOps:
* @param ctor the constructor
*/
def effectiveResultType(ctor: Symbol, paramss: List[List[Symbol]])(using Context): Type =
paramss match
case TypeSymbols(tparams) :: _ => ctor.owner.typeRef.appliedTo(tparams.map(_.typeRef))
case _ => ctor.owner.typeRef
val (resType, termParamss) = paramss match
case TypeSymbols(tparams) :: rest =>
(ctor.owner.typeRef.appliedTo(tparams.map(_.typeRef)), rest)
case _ =>
(ctor.owner.typeRef, paramss)
termParamss.flatten.foldLeft(resType): (rt, param) =>
if param.is(Tracked) then RefinedType(rt, param.name, param.termRef)
else rt

/** Split dependent class refinements off parent type. Add them to `refinements`,
* unless it is null.
*/
extension (tp: Type)
def separateRefinements(cls: ClassSymbol, refinements: mutable.LinkedHashMap[Name, Type] | Null)(using Context): Type =
tp match
case RefinedType(tp1, rname, rinfo) =>
try tp1.separateRefinements(cls, refinements)
finally
if refinements != null then
refinements(rname) = refinements.get(rname) match
case Some(tp) => tp & rinfo
case None => rinfo
case tp @ AnnotatedType(tp1, ann) =>
tp.derivedAnnotatedType(tp1.separateRefinements(cls, refinements), ann)
case tp: RecType =>
tp.parent.substRecThis(tp, cls.thisType).separateRefinements(cls, refinements)
case tp =>
tp

/** If isConstructor, make sure it has at least one non-implicit parameter list
* This is done by adding a () in front of a leading old style implicit parameter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,6 @@ trait PatternTypeConstrainer { self: TypeComparer =>
}
}

def stripRefinement(tp: Type): Type = tp match {
case tp: RefinedOrRecType => stripRefinement(tp.parent)
case tp => tp
}

def tryConstrainSimplePatternType(pat: Type, scrut: Type) = {
val patCls = pat.classSymbol
val scrCls = scrut.classSymbol
Expand Down Expand Up @@ -181,14 +176,14 @@ trait PatternTypeConstrainer { self: TypeComparer =>
case AndType(scrut1, scrut2) =>
constrainPatternType(pat, scrut1) && constrainPatternType(pat, scrut2)
case scrut: RefinedOrRecType =>
constrainPatternType(pat, stripRefinement(scrut))
constrainPatternType(pat, scrut.stripRefinement)
case scrut => dealiasDropNonmoduleRefs(pat) match {
case OrType(pat1, pat2) =>
either(constrainPatternType(pat1, scrut), constrainPatternType(pat2, scrut))
case AndType(pat1, pat2) =>
constrainPatternType(pat1, scrut) && constrainPatternType(pat2, scrut)
case pat: RefinedOrRecType =>
constrainPatternType(stripRefinement(pat), scrut)
constrainPatternType(pat.stripRefinement, scrut)
case pat =>
tryConstrainSimplePatternType(pat, scrut)
|| classesMayBeCompatible && constrainUpcasted(scrut)
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ object StdNames {
val RootPackage: N = "RootPackage"
val RootClass: N = "RootClass"
val Select: N = "Select"
val Self: N = "Self"
val Shape: N = "Shape"
val StringContext: N = "StringContext"
val This: N = "This"
Expand Down Expand Up @@ -452,6 +453,7 @@ object StdNames {
val create: N = "create"
val currentMirror: N = "currentMirror"
val curried: N = "curried"
val deferred: N = "deferred"
val definitions: N = "definitions"
val delayedInit: N = "delayedInit"
val delayedInitArg: N = "delayedInit$body"
Expand Down Expand Up @@ -623,6 +625,7 @@ object StdNames {
val toString_ : N = "toString"
val toTypeConstructor: N = "toTypeConstructor"
val tpe : N = "tpe"
val tracked: N = "tracked"
val transparent : N = "transparent"
val tree : N = "tree"
val true_ : N = "true"
Expand Down
8 changes: 6 additions & 2 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1185,13 +1185,17 @@ object SymDenotations {
final def isExtensibleClass(using Context): Boolean =
isClass && !isOneOf(FinalOrModuleClass) && !isAnonymousClass

/** A symbol is effectively final if it cannot be overridden in a subclass */
/** A symbol is effectively final if it cannot be overridden */
final def isEffectivelyFinal(using Context): Boolean =
isOneOf(EffectivelyFinalFlags)
|| is(Inline, butNot = Deferred)
|| is(JavaDefinedVal, butNot = Method)
|| isConstructor
|| !owner.isExtensibleClass
|| !owner.isExtensibleClass && !is(Deferred)
// Deferred symbols can arise through parent refinements.
// For them, the overriding relationship reverses anyway, so
// being in a final class does not mean the symbol cannot be
// implemented concretely in a superclass.

/** A class is effectively sealed if has the `final` or `sealed` modifier, or it
* is defined in Scala 3 and is neither abstract nor open.
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,6 @@ object Symbols extends SymUtils {
* With the given setup, all such calls will give implicit-not found errors
*/
final def symbol(implicit ev: DontUseSymbolOnSymbol): Nothing = unsupported("symbol")
type DontUseSymbolOnSymbol

final def source(using Context): SourceFile = {
def valid(src: SourceFile): SourceFile =
Expand Down Expand Up @@ -376,13 +375,12 @@ object Symbols extends SymUtils {
flags: FlagSet = this.flags,
info: Type = this.info,
privateWithin: Symbol = this.privateWithin,
coord: Coord = NoCoord, // Can be `= owner.coord` once we bootstrap
compUnitInfo: CompilationUnitInfo | Null = null // Can be `= owner.associatedFile` once we bootstrap
coord: Coord = NoCoord, // Can be `= owner.coord` once we have new default args
compUnitInfo: CompilationUnitInfo | Null = null // Can be `= owner.compilationUnitInfo` once we have new default args
): Symbol = {
val coord1 = if (coord == NoCoord) owner.coord else coord
val compilationUnitInfo1 = if (compilationUnitInfo == null) owner.compilationUnitInfo else compilationUnitInfo


if isClass then
newClassSymbol(owner, name.asTypeName, flags, _ => info, privateWithin, coord1, compilationUnitInfo1)
else
Expand Down Expand Up @@ -910,6 +908,8 @@ object Symbols extends SymUtils {
case (x: Symbol) :: _ if x.isType => Some(xs.asInstanceOf[List[TypeSymbol]])
case _ => None

type DontUseSymbolOnSymbol

// ----- Locating predefined symbols ----------------------------------------

def requiredPackage(path: PreName)(using Context): TermSymbol = {
Expand Down
14 changes: 10 additions & 4 deletions compiler/src/dotty/tools/dotc/core/TypeUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import TypeErasure.ErasedValueType
import Types.*, Contexts.*, Symbols.*, Flags.*, Decorators.*
import Names.Name

class TypeUtils {
class TypeUtils:
/** A decorator that provides methods on types
* that are needed in the transformer pipeline.
*/
extension (self: Type) {
extension (self: Type)

def isErasedValueType(using Context): Boolean =
self.isInstanceOf[ErasedValueType]
Expand Down Expand Up @@ -150,5 +150,11 @@ class TypeUtils {
case _ =>
val cls = self.underlyingClassRef(refinementOK = false).typeSymbol
cls.isTransparentClass && (!traitOnly || cls.is(Trait))
}
}

/** Strip all outer refinements off this type */
def stripRefinement: Type = self match
case self: RefinedOrRecType => self.parent.stripRefinement
case seld => self

end TypeUtils

Loading