diff --git a/Compiler/Ast.fir b/Compiler/Ast.fir index 08d1baa1..86ec21d3 100644 --- a/Compiler/Ast.fir +++ b/Compiler/Ast.fir @@ -79,6 +79,14 @@ type TypeDecl( ) +impl Tokens[TypeDecl]: + firstToken(self: TypeDecl) TokenIdx: + self.prim_.unwrapOr(self.name.token) + + lastToken(self: TypeDecl) TokenIdx: + panic("TOOD") + + ## Constructors of a type declaration. #[derive(ToDoc)] type TypeDeclRhs: @@ -98,6 +106,14 @@ type ConDecl( ) +impl Tokens[ConDecl]: + firstToken(self: ConDecl) TokenIdx: + self.name.token + + lastToken(self: ConDecl) TokenIdx: + self._lastToken + + #[derive(ToDoc)] type ConFields: Empty @@ -724,7 +740,7 @@ type Constructor( userTyArgs: Option[TyArgs], ## Inferred type arguments of the constructor's type. Filled in by the type checker. - tyArgs: Vec[Type], + tyArgs: Vec[Ty], # In variant constructors we'll see a '~' before `ty`, so we can't use `ty` as the first token. _firstToken: TokenIdx, @@ -873,7 +889,7 @@ type VarExpr( userTyArgs: Option[TyArgs], ## Inferred type arguments of the variable. Filled in by the type checker. - tyArgs: Vec[Type], + tyArgs: Vec[Ty], ) diff --git a/Compiler/Defs.fir b/Compiler/Defs.fir index 61c1691f..98a3eafc 100644 --- a/Compiler/Defs.fir +++ b/Compiler/Defs.fir @@ -53,6 +53,12 @@ VarId.resolve(self, def: VarDefIdx): self._resolved = Option.Some(def) +VarId.def(self) VarDefIdx: + self._resolved.unwrapOrElse( + \(): panic("Term id (\``self.name`\`) is not resolved yet"), + ) + + #[derive(ToDoc)] type AssocVarId( ## Index of the associated member identifier's token in its module. @@ -161,30 +167,17 @@ type TyDefIdx( ## Name of the type in `_mod`. _name: Str, - - ## Kind of the definition: type or trait. - _kind: TyDefKind, ) -#[derive(ToDoc)] -type TyDefKind: - Type - Trait - - impl Eq[TyDefIdx]: __eq(self: TyDefIdx, other: TyDefIdx) Bool: - self._mod == other._mod - and self._name == other._name - and self._kind == other._kind + self._mod == other._mod and self._name == other._name -impl Eq[TyDefKind]: - __eq(self: TyDefKind, other: TyDefKind) Bool: - match self: - TyDefKind.Type: other is TyDefKind.Type - TyDefKind.Trait: other is TyDefKind.Trait +impl Hash[TyDefIdx]: + hash(self: TyDefIdx) U32: + self._mod.hash() + self._name.hash() # ------------------------------------------------------------------------------ @@ -317,21 +310,10 @@ impl ToDoc[TyDefIdx]: + Doc.nested(4, Doc.break_(1) + self._name.toDoc()), ) args += Doc.char(',') + Doc.break_(1) - args += Doc.grouped( - Doc.str("_kind =") - + Doc.nested(4, Doc.break_(1) + self._kind.toDoc()), - ) args = args.nest(4).group() + Doc.break_(0) + Doc.char(')') Doc.grouped(Doc.str("TyDefIdx") + Doc.char('(') + args) -impl ToDoc[TyDefKind]: - toDoc(self: TyDefKind) Doc: - match self: - TyDefKind.Type: Doc.str("TyDefKind.Type") - TyDefKind.Trait: Doc.str("TyDefKind.Trait") - - # ------------------------------------------------------------------------------ # ToStr implementations. These are used when generating error messages, so they # should be readable by the users and should not expose implementation details. diff --git a/Compiler/Module.fir b/Compiler/Module.fir index 5e942aef..b0af52ef 100644 --- a/Compiler/Module.fir +++ b/Compiler/Module.fir @@ -85,7 +85,7 @@ type Module( # modules. ## Term environment of the module, used for name resolving. - _termEnv: HashMap[Str, VarDefIdx], + _termEnv: HashMap[Str, TopVarDefIdx], ## Associated item environment of the module, used for name resolving. _assocTermEnv: HashMap[Str, HashMap[Str, AssocVarDefIdx]], @@ -124,12 +124,44 @@ Module.loc[Tokens[t]](self, t: t) Loc: Module.tyCon(self, tyIdx: TyDefIdx) TyCon: assert(self._idx == tyIdx._mod) - let tcEnv = self._tcEnv.unwrap() - let item = tcEnv._cons.get(tyIdx._name).unwrap() + + let tcEnv = self._tcEnv.unwrapOrElse( + \(): panic("Module.tyCon called before preparing type environments"), + ) + + # `TyDefIdx`s are assigned by the name resolver after finding the + # definition, so it's a bug if a `TyDefIdx` is not in the environment. + let item = tcEnv._cons.get(tyIdx._name).unwrapOrElse( + \(): + panic( + "BUG: Module.tyCon TyDefIdx is not in the module type checking environment", + ), + ) + assert(item.idx == tyIdx) item.info +Module.scheme(self, varIdx: TopVarDefIdx) Scheme: + assert(self._idx == varIdx._mod) + + let tcEnv = self._tcEnv.unwrapOrElse( + \(): panic("Module.scheme called before preparing type environments"), + ) + + # `TopVarDefIdx`s are assigned by the name resolver after finding the + # definition, so it's a bug if a `TopVarDefIdx` is not in the environment. + let item = tcEnv._topSchemes.get(varIdx._name).unwrapOrElse( + \(): + panic( + "BUG: Module.scheme TopVarDefIdx is not in the module type checking environment", + ), + ) + + assert(item.idx == varIdx) + item.info + + # ------------------------------------------------------------------------------ # Generated ToDoc implementations diff --git a/Compiler/NameResolver.fir b/Compiler/NameResolver.fir index 8ac690b6..ca802fd4 100644 --- a/Compiler/NameResolver.fir +++ b/Compiler/NameResolver.fir @@ -15,7 +15,14 @@ resolveNames(pgm: Program): _prepRenamingEnvs(pgm) for module: Module in pgm._modules.iter(): - let termEnv = ScopeMap.fromMap(module._termEnv) + let termEnv = ScopeMap.fromMap( + HashMap.fromIter( + module._termEnv.iter().map( + \(entry: HashMapEntry[Str, TopVarDefIdx]): + (key = entry.key, value = VarDefIdx.Top(entry.value)), + ), + ), + ) let visitor = _NameResolverVisitor( _moduleFilePath = module._filePath, @@ -220,14 +227,14 @@ _makeSccDepGraph(pgm: Program, sccs: Vec[HashSet[ModuleIdx]]) Vec[HashSet[U32]]: type _SccEnv( - _termEnv: HashMap[Str, VarDefIdx], + _termEnv: HashMap[Str, TopVarDefIdx], _assocTermEnv: HashMap[Str, HashMap[Str, AssocVarDefIdx]], _tyEnv: HashMap[Str, TyDefIdx], ) _SccEnv.merge(self, other: _SccEnv): - for term: HashMapEntry[Str, VarDefIdx] in other._termEnv.iter(): + for term: HashMapEntry[Str, TopVarDefIdx] in other._termEnv.iter(): let old = self._termEnv.insert(term.key, term.value) if old is Option.Some(oldImport) and oldImport != term.value: panic("Variable `term.key` defined multiple times in SCC") @@ -264,7 +271,7 @@ _makeSccEnv( if sccEnvs.get(sccIdx) is Option.Some(env): return env - let termEnv: HashMap[Str, VarDefIdx] = HashMap.withCapacity(50) + let termEnv: HashMap[Str, TopVarDefIdx] = HashMap.withCapacity(50) let assocTermEnv: HashMap[Str, HashMap[Str, AssocVarDefIdx]] = HashMap .withCapacity(25) let typeEnv: HashMap[Str, TyDefIdx] = HashMap.withCapacity(25) @@ -276,11 +283,7 @@ _makeSccEnv( let tokens = mod._tokens for ty: HashMapEntry[Str, TypeDecl] in mod._tyItems.iter(): - let tyDefIdx = TyDefIdx( - _mod = modIdx, - _name = ty.key, - _kind = TyDefKind.Type, - ) + let tyDefIdx = TyDefIdx(_mod = modIdx, _name = ty.key,) let old = typeEnv.insert(ty.key, tyDefIdx) if old is Option.Some(_): @@ -289,11 +292,7 @@ _makeSccEnv( ) for trait_: HashMapEntry[Str, TraitDecl] in mod._traitItems.iter(): - let traitDefIdx = TyDefIdx( - _mod = modIdx, - _name = trait_.key, - _kind = TyDefKind.Trait, - ) + let traitDefIdx = TyDefIdx(_mod = modIdx, _name = trait_.key,) let old = typeEnv.insert(trait_.key, traitDefIdx) if old is Option.Some(_): panic( @@ -301,9 +300,8 @@ _makeSccEnv( ) for fun: HashMapEntry[Str, FunDecl] in mod._funItems.iter(): - let varDefIdx = VarDefIdx.Top( - TopVarDefIdx(_mod = modIdx, _name = fun.key), - ) + let varDefIdx = TopVarDefIdx(_mod = modIdx, _name = fun.key) + let old = termEnv.insert(fun.key, varDefIdx) if old is Option.Some(_): panic( diff --git a/Compiler/Program.fir b/Compiler/Program.fir index c257231c..a0859111 100644 --- a/Compiler/Program.fir +++ b/Compiler/Program.fir @@ -27,11 +27,20 @@ Program.new() Program: Program(_modules = Vec.empty(), _depGraph = Option.None,) +Program.module(self, modIdx: ModuleIdx) Module: + self._modules.get(modIdx._idx) + + Program.tyCon(self, tyIdx: TyDefIdx) TyCon: let mod = self._modules.get(tyIdx._mod._idx) mod.tyCon(tyIdx) +Program.scheme(self, varIdx: TopVarDefIdx) Scheme: + let mod = self._modules.get(varIdx._mod._idx) + mod.scheme(varIdx) + + ## Load a module, or return it from the cache if it's already loaded. ## ## Follows imports in the module to transitively import all referred modules. diff --git a/Compiler/TypeCheck.fir b/Compiler/TypeCheck.fir index 22a5d62a..fe0713b5 100644 --- a/Compiler/TypeCheck.fir +++ b/Compiler/TypeCheck.fir @@ -4,6 +4,7 @@ import [ Compiler/ScopeMap, Compiler/TypeCheck/Convert, Compiler/TypeCheck/Error, + Compiler/TypeCheck/Expr, Compiler/TypeCheck/KindInference, Compiler/TypeCheck/Normalization, Compiler/TypeCheck/RowUtils, @@ -221,31 +222,101 @@ addModTyConDetails(pgm: Program, modIdx: ModuleIdx) / TypeError: for tyDecl: TypeDecl in mod._tyItems.values(): let tyDeclName = mod.idText(tyDecl.name) let tyCon = tcEnv._cons.get(tyDeclName).unwrap() - let details = TyConDetails.Type( - TypeDetails( - cons = - match tyDecl.rhs: - Option.Some(TypeDeclRhs.Sum(cons)): - Vec.fromIter( - cons.iter().map( - \(con: ConDecl) Str: mod.idText(con.name), + let varMap: ScopeMap[Str, Ty] = ScopeMap.empty() + + assert(tyDecl.typeParams.len() == tyDecl.typeParamKinds.len()) + + let typeContext: Vec[Pred] = convertAndBindContext( + mod, + varMap, + Vec.fromIter( + range(u32(0), tyDecl.typeParams.len()).map( + \(i): + TypeParam( + name = + LocalId( + name = mod.idText(tyDecl.typeParams.get(i)), ), - ) - Option.Some(TypeDeclRhs.Product(_fields)): - Vec.[tyDeclName] - Option.None: Vec.[], + kind = tyDecl.typeParamKinds.get(i), + ), + ), + ), + Vec.empty(), + TyVarConversion.ToQVar, + ) + + assert(typeContext.isEmpty()) + + let schemes: HashMap[Str, Scheme] = HashMap.empty() + + # Return type of constructors. + let tyNameStr = mod.idText(tyDecl.name) + let tyDefIdx = mod._tyEnv.get(tyNameStr).unwrap() + let qvars = Vec.fromIter( + range(u32(0), tyDecl.typeParams.len()).map( + \(typeParamIdx): + let paramNameStr = mod.idText( + tyDecl.typeParams.get(typeParamIdx), + ) + QVar( + id = LocalId(name = paramNameStr), + kind = tyDecl.typeParamKinds.get(typeParamIdx), + ), + ), + ) + + let ret = if tyDecl.typeParams.isEmpty(): + Ty.Con(id = tyDefIdx) + else: + Ty.App( + conId = tyDefIdx, + args = Vec.fromIter(qvars.iter().map(Ty.QVar)), + ) + + match tyDecl.rhs: + Option.Some(TypeDeclRhs.Sum(cons)): + for con: ConDecl in cons.iter(): + let conName = mod.idText(con.name) + let ty = match convertFields(mod, varMap, con.fields): + Option.None: ret + Option.Some(args): Ty.Fun(args, ret, exn = Option.None) + let scheme = Scheme( + qvars, + preds = Vec.empty(), + ty, + loc = mod.loc(con), + ) + let old = schemes.insert(conName, scheme) + assert(old is Option.None) + + Option.Some(TypeDeclRhs.Product(fields)): + let ty = match convertFields(mod, varMap, fields): + Option.None: ret + Option.Some(args): Ty.Fun(args, ret, exn = Option.None) + let scheme = Scheme( + qvars, + preds = Vec.empty(), + ty, + loc = mod.loc(tyDecl), + ) + let old = schemes.insert(tyNameStr, scheme) + assert(old is Option.None) + + Option.None: () + + tyCon.info.details = TyConDetails.Type( + TypeDetails( + cons = schemes, sum = not (tyDecl.rhs is Option.Some(TypeDeclRhs.Product(_fields))), ), ) - tyCon.info.details = details for traitDecl: TraitDecl in mod._traitItems.values(): let tyDeclName = mod.idText(traitDecl.name) let tyCon = tcEnv._cons.get(tyDeclName).unwrap() let varMap: ScopeMap[Str, Ty] = ScopeMap.empty() - # TODO: This doesn't hold, need to add kinds. assert(traitDecl.typeParams.len() == traitDecl.typeParamKinds.len()) let traitContext: Vec[Pred] = convertAndBindContext( @@ -378,143 +449,206 @@ addModSchemes(pgm: Program, modIdx: ModuleIdx) / TypeError: let mod = pgm._modules.get(modIdx._idx) let tcEnv = mod._tcEnv.unwrap() - for topFun: FunDecl in mod._funItems.values(): - let varMap: ScopeMap[Str, Ty] = ScopeMap.empty() + for funDecl: FunDecl in mod._funItems.values(): + _addFunDeclScheme(funDecl, pgm, modIdx) - let funPreds: Vec[Pred] = convertAndBindContext( - mod, - varMap, - topFun.sig.typeParams, - topFun.sig.context.map(\(ctx: Context): ctx.preds).unwrapOr( - Vec.empty(), - ), - TyVarConversion.ToQVar, - ) + for tyDecl: TypeDecl in mod._tyItems.values(): + _addTySchemes(tyDecl, pgm, modIdx) - let numQVars = topFun.sig.typeParams.len() - # Not specifying the exception type is the same as having a fresh - # type variable for the exception type. - if topFun.sig.exceptions is Option.None: - numQVars += 1 +_addFunDeclScheme(topFun: FunDecl, pgm: Program, modIdx: ModuleIdx) / TypeError: + let mod = pgm.module(modIdx) + let tcEnv = mod._tcEnv.unwrap() - let schemeQVars: Vec[QVar] = Vec.withCapacity(numQVars) + let varMap: ScopeMap[Str, Ty] = ScopeMap.empty() - for tyParam: TypeParam in topFun.sig.typeParams.iter(): - schemeQVars.push(QVar(id = tyParam.name, kind = tyParam.kind)) + let funPreds: Vec[Pred] = convertAndBindContext( + mod, + varMap, + topFun.sig.typeParams, + topFun.sig.context.map(\(ctx: Context): ctx.preds).unwrapOr(Vec.empty()), + TyVarConversion.ToQVar, + ) - if topFun.sig.exceptions is Option.None: - # The name we invent for the exception type does not matter as - # it can't be used in the function body. It's also OK to not - # bind it in `convertAndBindContext` above for the same reason. - schemeQVars.push( - QVar(id = LocalId(name = "?exn"), kind = Kind.Star), - ) + let numQVars = topFun.sig.typeParams.len() - let argTys: Vec[Ty] = Vec.fromIter( - topFun.sig.params.iter().map( - \(param: FunArg) Ty: - let ty = param.ty.unwrap() - convertAstTy(mod, varMap, ty), - ), - ) + # Not specifying the exception type is the same as having a fresh type + # variable for the exception type. + if topFun.sig.exceptions is Option.None: + numQVars += 1 - match topFun.sig.self_: - SelfParam.No: () - SelfParam.Implicit: - # Parent type should have no type arguments. - match topFun.parentTy: - Option.None: - # TODO: location should be the `self` location, but we - # don't have easy access to it yet. + let schemeQVars: Vec[QVar] = Vec.withCapacity(numQVars) + + for tyParam: TypeParam in topFun.sig.typeParams.iter(): + schemeQVars.push(QVar(id = tyParam.name, kind = tyParam.kind)) + + if topFun.sig.exceptions is Option.None: + # The name we invent for the exception type does not matter as it can't + # be used in the function body. It's also OK to not bind it in + # `convertAndBindContext` above for the same reason. + schemeQVars.push(QVar(id = LocalId(name = "?exn"), kind = Kind.Star)) + + let argTys: Vec[Ty] = Vec.fromIter( + topFun.sig.params.iter().map( + \(param: FunArg) Ty: + let ty = param.ty.unwrap() + convertAstTy(mod, varMap, ty), + ), + ) + + match topFun.sig.self_: + SelfParam.No: () + SelfParam.Implicit: + # Parent type should have no type arguments. + match topFun.parentTy: + Option.None: + # TODO: location should be the `self` location, but we don't + # have easy access to it yet. + throw( + TypeError( + loc = mod.tokenLoc(topFun.name.token), + msg = + "Function with \`self\` parameter should be an associated type or method", + ), + ) + Option.Some(parentTy): + let parentTyDef = parentTy.def() + let parentTyCon = pgm.tyCon(parentTyDef) + if not parentTyCon.tyParams.isEmpty(): throw( TypeError( loc = mod.tokenLoc(topFun.name.token), msg = - "Function with \`self\` parameter should be an associated type or method", + "Function \`self\` type needs type annotation", ), ) - Option.Some(parentTy): - let parentTyDef = parentTy.def() - let parentTyCon = pgm.tyCon(parentTyDef) - if not parentTyCon.tyParams.isEmpty(): - throw( - TypeError( - loc = mod.tokenLoc(topFun.name.token), - msg = - "Function \`self\` type needs type annotation", - ), - ) - argTys.insert(0, Ty.Con(id = parentTyDef)) - SelfParam.Explicit(ty): - argTys.insert(0, convertAstTy(mod, varMap, ty)) + argTys.insert(0, Ty.Con(id = parentTyDef)) + SelfParam.Explicit(ty): argTys.insert(0, convertAstTy(mod, varMap, ty)) - let retTy: Ty = match topFun.sig.returnTy: - Option.None: Ty.unit() - Option.Some(retTy): convertAstTy(mod, varMap, retTy) + let retTy: Ty = match topFun.sig.returnTy: + Option.None: Ty.unit() + Option.Some(retTy): convertAstTy(mod, varMap, retTy) - let exnTy: Ty = match topFun.sig.exceptions: - Option.None: Ty.QVar(schemeQVars.last().unwrap()) - Option.Some(retTy): convertAstTy(mod, varMap, retTy) + let exnTy: Ty = match topFun.sig.exceptions: + Option.None: Ty.QVar(schemeQVars.last().unwrap()) + Option.Some(retTy): convertAstTy(mod, varMap, retTy) - assert(varMap.lenScopes() == 1) + assert(varMap.lenScopes() == 1) - let funTy = Ty.Fun( - args = FunArgs.Positional(argTys), - ret = retTy, - exn = Option.Some(exnTy), - ) + let funTy = Ty.Fun( + args = FunArgs.Positional(argTys), + ret = retTy, + exn = Option.Some(exnTy), + ) - let scheme = Scheme( - qvars = schemeQVars, - preds = funPreds, - ty = funTy, - loc = mod.loc(topFun), - ) + let scheme = Scheme( + qvars = schemeQVars, + preds = funPreds, + ty = funTy, + loc = mod.loc(topFun), + ) + + let funName = mod.idText(topFun.name) + + match topFun.parentTy: + Option.Some(parentTy): + let parentTyName = parentTy.name - let funName = mod.idText(topFun.name) - - match topFun.parentTy: - Option.Some(parentTy): - let parentTyName = parentTy.name - - let assocFunMap = match tcEnv._assocFnSchemes.get(parentTyName): - Option.Some(methods): methods - Option.None: - let methods: HashMap[Str, TcItem[AssocVarDefIdx, Scheme]] = HashMap - .withCapacity(10) - tcEnv._assocFnSchemes.insert(parentTyName, methods) - methods - - let old = assocFunMap.insert( - funName, - TcItem( - idx = - mod._assocTermEnv.get(parentTyName).unwrap().get( - funName, - ).unwrap(), - info = scheme, - imports = HashSet.[modIdx], + let assocFunMap = match tcEnv._assocFnSchemes.get(parentTyName): + Option.Some(methods): methods + Option.None: + let methods: HashMap[Str, TcItem[AssocVarDefIdx, Scheme]] = HashMap + .withCapacity(10) + tcEnv._assocFnSchemes.insert(parentTyName, methods) + methods + + let old = assocFunMap.insert( + funName, + TcItem( + idx = + mod._assocTermEnv.get(parentTyName).unwrap().get( + funName, + ).unwrap(), + info = scheme, + imports = HashSet.[modIdx], + ), + ) + + Option.None: + let old = tcEnv._topSchemes.insert( + funName, + TcItem( + idx = mod._termEnv.get(funName).unwrap(), + info = scheme, + imports = HashSet.[modIdx], + ), + ) + if old is Option.Some(..): + throw( + TypeError( + loc = mod.tokenLoc(topFun.name.token), + msg = + "Top-level function \``funName`\` defined multiple times", ), ) - Option.None: - let old = tcEnv._topSchemes.insert( - funName, - TcItem( - idx = mod._termEnv.get(funName).unwrap(), - info = scheme, - imports = HashSet.[modIdx], + +_addTySchemes(tyDecl: TypeDecl, pgm: Program, modIdx: ModuleIdx) / TypeError: + let rhs = match tyDecl.rhs: + Option.None: return + Option.Some(rhs): rhs + + let mod = pgm.module(modIdx) + + let varMap: ScopeMap[Str, Ty] = ScopeMap.empty() + + # Bind type parameters in the context for constructor schemes. + + # If this fails then the kind inference is buggy: + assert(tyDecl.typeParams.len() == tyDecl.typeParamKinds.len()) + + for typeParamIdx: U32 in range(u32(0), tyDecl.typeParams.len()): + let paramNameStr = mod.idText(tyDecl.typeParams.get(typeParamIdx)) + varMap.insert( + paramNameStr, + Ty.QVar( + QVar( + id = LocalId(name = paramNameStr), + kind = tyDecl.typeParamKinds.get(typeParamIdx), + ), + ), + ) + + # Return type of constructors. + let tyNameStr = mod.idText(tyDecl.name) + let tyDefIdx = mod._tyEnv.get(tyNameStr).unwrap() + let ret = if tyDecl.typeParams.isEmpty(): + Ty.Con(id = tyDefIdx) + else: + Ty.App( + conId = tyDefIdx, + args = + Vec.fromIter( + range(u32(0), tyDecl.typeParams.len()).map( + \(typeParamIdx: U32) Ty: + let paramNameStr = mod.idText( + tyDecl.typeParams.get(typeParamIdx), + ) + Ty.QVar( + QVar( + id = LocalId(name = paramNameStr), + kind = + tyDecl.typeParamKinds.get(typeParamIdx), + ), + ), ), - ) - if old is Option.Some(..): - throw( - TypeError( - loc = mod.tokenLoc(topFun.name.token), - msg = - "Top-level function \``funName`\` defined multiple times", - ), - ) + ), + ) + + match rhs: + TypeDeclRhs.Sum(cons): print("TODO: TypeDeclRhs.Sum") + + TypeDeclRhs.Product(fields): print("TODO: TypeDeclRhs.Product") ## Update SCC modules with imported top-level and associated function schemes. @@ -533,7 +667,7 @@ addImportedSchemes(pgm: Program, sccModules: HashSet[ModuleIdx]): let importedModTcEnv = importedMod._tcEnv.unwrap() # Import top-level functions. - for topFun: HashMapEntry[Str, TcItem[VarDefIdx, Scheme]] in + for topFun: HashMapEntry[Str, TcItem[TopVarDefIdx, Scheme]] in importedModTcEnv._topSchemes.iter(): match modTcEnv._topSchemes.get(topFun.key): Option.Some(existingItem): @@ -618,7 +752,7 @@ type ModuleTcEnv( _cons: HashMap[Str, TcItem[TyDefIdx, TyCon]], ## Top-level function schemes. - _topSchemes: HashMap[Str, TcItem[VarDefIdx, Scheme]], + _topSchemes: HashMap[Str, TcItem[TopVarDefIdx, Scheme]], ## Associated function schemes. _assocFnSchemes: HashMap[Str, HashMap[Str, TcItem[AssocVarDefIdx, Scheme]]], @@ -684,6 +818,8 @@ ModuleTcEnv.empty() ModuleTcEnv: ## Type checking state for a single function (top-level, associated, or method). type FunTcEnv( + _modIdx: ModuleIdx, + _modEnv: ModuleTcEnv, _termEnv: ScopeMap[Str, Ty], @@ -719,8 +855,15 @@ type FunTcEnv( ) -FunTcEnv.new(modEnv: ModuleTcEnv, exnTy: Ty, retTy: Ty, assumps: Vec[Pred]) FunTcEnv: +FunTcEnv.new( + modIdx: ModuleIdx, + modEnv: ModuleTcEnv, + exnTy: Ty, + retTy: Ty, + assumps: Vec[Pred], +) FunTcEnv: FunTcEnv( + _modIdx = modIdx, _modEnv = modEnv, _termEnv = ScopeMap.empty(), _varGen = UVarGen.new(), @@ -731,6 +874,10 @@ FunTcEnv.new(modEnv: ModuleTcEnv, exnTy: Ty, retTy: Ty, assumps: Vec[Pred]) FunT ) +FunTcEnv.local(self, local: Str) Ty: + self._termEnv.get(local).unwrap() + + # ------------------------------------------------------------------------------ # Generated ToDoc implementations diff --git a/Compiler/TypeCheck/Convert.fir b/Compiler/TypeCheck/Convert.fir index e2bac969..a9bbd6ea 100644 --- a/Compiler/TypeCheck/Convert.fir +++ b/Compiler/TypeCheck/Convert.fir @@ -61,23 +61,47 @@ convertAstTy(module: Module, vars: ScopeMap[Str, Ty], astTy: Type) Ty / TypeErro ), ) - Ty.Anonymous(labels, extension, kind = RecordOrVariant.Record, isRow) + Ty.Record(labels, extension, isRow) Type.Variant(VariantType(alts, extension, isRow, ..)): - let labels: HashMap[LocalId, Ty] = HashMap.withCapacity(alts.len()) + let cons: HashMap[TyDefIdx, Vec[Ty]] = HashMap.withCapacity( + alts.len(), + ) for alt: NamedType in alts.iter(): let ty = convertNamedTy(module, vars, alt, loc) - let tyNameText = alt.name.name - let old = labels.insert(LocalId(name = tyNameText), ty) - if old is Option.Some(_): - throw( - TypeError( - loc, - msg = - "Type `tyNameText` used multiple times in variant type", - ), - ) + + match ty: + Ty.Con(id): + let old = cons.insert(id, Vec.empty()) + if old is Option.Some(_): + throw( + TypeError( + loc, + msg = + "Type `alt.name.name` used multiple times in variant type", + ), + ) + + Ty.App(conId, args): + let old = cons.insert(conId, Vec.empty()) + if old is Option.Some(_): + throw( + TypeError( + loc, + msg = + "Type `alt.name.name` used multiple times in variant type", + ), + ) + + other: + throw( + TypeError( + loc, + msg = + "Variants can't have type variables, functions, records, variants as alternatives", + ), + ) let extension = extension.map( \(extId): @@ -93,12 +117,7 @@ convertAstTy(module: Module, vars: ScopeMap[Str, Ty], astTy: Type) Ty / TypeErro ), ) - Ty.Anonymous( - labels, - extension, - kind = RecordOrVariant.Variant, - isRow, - ) + Ty.Variant(cons, extension, isRow,) Type.Fn_(FnType(args, ret, exceptions, ..)): let args = FunArgs.Positional( @@ -165,6 +184,38 @@ convertNamedTy( Ty.App(conId = tyDefIdx, args = convertedArgs) +convertFields(module: Module, vars: ScopeMap[Str, Ty], fields: ConFields) Option[FunArgs] / TypeError: + match fields: + ConFields.Empty: Option.None + + ConFields.Named(namedFields): + Option.Some( + FunArgs.Named( + HashMap.fromIter( + namedFields.iter().map( + \(field: NamedField) (key: LocalId, value: Ty): + ( + key = + LocalId(name = module.idText(field.name)), + value = convertAstTy(module, vars, field.ty), + ), + ), + ), + ), + ) + + ConFields.Unnamed(fields): + Option.Some( + FunArgs.Positional( + Vec.fromIter( + fields.iter().map( + \(ty: Type) Ty: convertAstTy(module, vars, ty), + ), + ), + ), + ) + + type TyVarConversion: ToRVar ToQVar diff --git a/Compiler/TypeCheck/Expr.fir b/Compiler/TypeCheck/Expr.fir new file mode 100644 index 00000000..705c88fd --- /dev/null +++ b/Compiler/TypeCheck/Expr.fir @@ -0,0 +1,266 @@ +import [Compiler/Ast] + + +# TODO: the returned types in `checkExpr` should be unified with the expected +# type. Maybe have two `checkExpr`s: one that always returns the inferred type, +# and another that takes the inferred type nad unifies with the expected type. + + +## Returns the type of the expression, and binders that the expression binds. +## +## Only boolean expressions bind variables. +## +## - ` is ` binds the variables that `` binds. +## +## - ` and ` binds the variables that `` and `` +## bind. `` and `` need to bind disjoint set of variables. +## +## Other expressions don't bind any variables. +## +## Variables bound in `if` and `while` conditionals are used in the bodies. +checkExpr( + expr: Expr, + pgm: Program, + env: FunTcEnv, + expectedTy: Option[Ty], + level: U32, + loopStack: Option[Vec[LocalId]], +) (ty: Ty, binds: HashMap[LocalId, Ty]) / TypeError: + match expr: + Expr.Var(expr): + _checkVarExpr(expr, pgm, env, expectedTy, level, loopStack) + + Expr.ConstrSelect(expr): + _checkConExpr(expr, pgm, env, expectedTy, level, loopStack) + + Expr.FieldSelect(..): panic("TODO") + + Expr.MethodSelect(..): panic("TODO") + + Expr.ConstrSelect(..): panic("TODO") + + Expr.AssocFnSelect(..): panic("TODO") + + Expr.Call(..): panic("TODO") + + Expr.Int(..): panic("TODO") + + Expr.Str(..): panic("TODO") + + Expr.Char(..): panic("TODO") + + Expr.Self(..): panic("TODO") + + Expr.BinOp(..): panic("TODO") + + Expr.UnOp(..): panic("TODO") + + Expr.Record(..): panic("TODO") + + Expr.Return(..): panic("TODO") + + Expr.Match(..): panic("TODO") + + Expr.If(..): panic("TODO") + + Expr.Fn_(..): panic("TODO") + + Expr.Is(..): panic("TODO") + + Expr.Do(..): panic("TODO") + + Expr.Seq(..): panic("TODO") + + Expr.Paren(..): panic("TODO") + + +_checkVarExpr( + varExpr: VarExpr, + pgm: Program, + env: FunTcEnv, + expectedTy: Option[Ty], + level: U32, + loopStack: Option[Vec[LocalId]], +) (ty: Ty, binds: HashMap[LocalId, Ty]) / TypeError: + let mod = pgm.module(env._modIdx) + let id = varExpr.id + let userTyArgs = varExpr.userTyArgs + let exprLoc = mod.loc(varExpr) + + assert(varExpr.tyArgs.isEmpty()) + + match id.def(): + VarDefIdx.Local: + if userTyArgs is Option.Some(..): + throw( + TypeError( + loc = exprLoc, + msg = + "Local variables can't have type parameters, but \``id.name`\`is passed type arguments", + ), + ) + (ty = env.local(id.name), binds = HashMap.empty()) + + VarDefIdx.Top(topVarIdx): + let scheme = pgm.scheme(topVarIdx) + + let ty = if userTyArgs is Option.Some(userTyArgs): + # This should be syntax error: + assert(not userTyArgs.args.isEmpty()) + + if scheme.qvars.len() != userTyArgs.args.len(): + throw( + TypeError( + loc = exprLoc, + msg = + "Variable \``id.name`\` takes `scheme.qvars.len()` type arguments, but applied to `userTyArgs.args.len()`", + ), + ) + + let userTyArgsConverted: Vec[Ty] = Vec.fromIter( + userTyArgs.args.iter().map( + \(ty: Type) Ty: convertAstTy(mod, env._termEnv, ty,), + ), + ) + + let ty = scheme.instantiateWithTys( + userTyArgsConverted, + env._preds, + exprLoc, + ) + + varExpr.tyArgs = userTyArgsConverted + ty + else: + let tyAndVars = scheme.instantiate( + level, + env._varGen, + env._preds, + exprLoc, + ) + varExpr.tyArgs = Vec.fromIter(tyAndVars.vars.iter().map(Ty.UVar)) + tyAndVars.ty + + (ty = ty, binds = HashMap.empty()) + + +_checkConExpr( + con: Constructor, + pgm: Program, + env: FunTcEnv, + expectedTy: Option[Ty], + level: U32, + loopStack: Option[Vec[LocalId]], +) (ty: Ty, binds: HashMap[LocalId, Ty]) / TypeError: + assert(con.tyArgs.isEmpty()) + + let mod = pgm.module(env._modIdx) + + let tyCon = pgm.tyCon(con.ty.def()) + let tyConUseSiteName = mod.tokenText(con.ty.token) + + let details = match tyCon.details: + TyConDetails.Trait(..): + throw( + TypeError( + loc = mod.loc(con), + msg = "Type in constructor selection is a trait", + ), + ) + + TyConDetails.Type(details): details + + # TODO: This does not support renaming constructors + let conName = con.constr.map(\(id): mod.idText(id)).unwrapOrElse( + \(): tyCon.name, + ) + + let scheme = details.cons.get(conName).unwrapOrElse( + \(): + throw( + TypeError( + loc = mod.loc(con), + msg = + "Type `tyConUseSiteName` does not have constructor `conName`", + ), + ), + ) + + let conTy: Ty = match con.userTyArgs: + Option.None: + let (ty, vars) = scheme.instantiate( + level, + env._varGen, + env._preds, + mod.loc(con), + ) + + con.tyArgs = Vec.fromIter(vars.iter().map(Ty.UVar)) + + if con.variant: + () + else: + () + + ty + + Option.Some(args): + assert(not args.args.isEmpty()) + panic("TODO") + + panic("TODO") + + +# This function is ported from the interpreter and it's not quite right. +# +# In the interpreter we parse `~C(...)` as a constructor application, rather +# than a variant expression. The fact that this is a variant is reflected in the +# constructor AST. +# +# So we type check `App(~C, ...)` instead of `Var(App(C, ...))`, and then apply +# it to the arguments, and the return type should be a variant of `C`, not just +# `C`. +# +# In the compiler we should parse this as `Var(App(Con(...), ...))`, and then +# have an "inject" function that takes a type (other than a function type) and +# convert it to a variant. +# +# Relevant issue: #238. +# +# Original documentation below: +# +# ty -> [labelOf(ty): ty, ..r] (r is fresh) +# +# Somewhat hackily, we also convert function types that return named types to +# function types that return variants instead, to allow type checking +# `~Foo(args)` by first converting `Foo`'s type to a function type, and then +# applying. +makeVariant(varGen: UVarGen, ty: Ty, level: U32, loc: Loc) Ty: + let extension = Option.Some( + Ty.UVar(varGen.newVar(level, Kind.Row(RecordOrVariant.Variant), loc)), + ) + + match ty.normalize(): + Ty.Con(id, ..): + Ty.Variant( + cons = HashMap.[id = Vec.empty()], + extension, + isRow = Bool.False, + ) + + Ty.App(conId, args): + Ty.Variant( + cons = HashMap.[conId = args], + extension, + isRow = Bool.False, + ) + + Ty.Fun(args, ret, exn): + # This part is also ported from the interpreter and while it's not + # wrong it's also too flexible/permissive. When the type is a + # function type the return type must be a `Con` or `App`, so we + # don't need a recursive call, which would handle `Fun`s in return + # type position. + return Ty.Fun(args, ret = makeVariant(varGen, ret, level, loc), exn) + + _: panic("Type in variant is not a constructor") diff --git a/Compiler/TypeCheck/Normalization.fir b/Compiler/TypeCheck/Normalization.fir index 3a8a4141..7f6da779 100644 --- a/Compiler/TypeCheck/Normalization.fir +++ b/Compiler/TypeCheck/Normalization.fir @@ -67,8 +67,29 @@ Ty.deepNormalize(self) Ty: exn = exn.map(\(ty: Ty): ty.deepNormalize()), ) - Ty.Anonymous(labels, extension, kind, isRow): - Ty.Anonymous( + Ty.Variant(cons, extension, isRow): + Ty.Variant( + cons = + HashMap.fromIter( + cons.iter().map( + \(label: HashMapEntry[TyDefIdx, Vec[Ty]]) (key: TyDefIdx, value: Vec[Ty]): + ( + key = label.key, + value = + Vec.fromIter( + label.value.iter().map( + \(ty: Ty) Ty: ty.deepNormalize(), + ), + ), + ), + ), + ), + extension = extension.map(\(ty: Ty): ty.deepNormalize()), + isRow, + ) + + Ty.Record(labels, extension, isRow): + Ty.Record( labels = HashMap.fromIter( labels.iter().map( @@ -80,6 +101,5 @@ Ty.deepNormalize(self) Ty: ), ), extension = extension.map(\(ty: Ty): ty.deepNormalize()), - kind, isRow, ) diff --git a/Compiler/TypeCheck/RowUtils.fir b/Compiler/TypeCheck/RowUtils.fir index 38a2364b..34b89b95 100644 --- a/Compiler/TypeCheck/RowUtils.fir +++ b/Compiler/TypeCheck/RowUtils.fir @@ -4,6 +4,7 @@ import [ ] +#| collectRows( ty: Ty, tyKind: RecordOrVariant, @@ -52,3 +53,4 @@ collectRows( other: return (rows = allLabels, extension = Option.Some(other)) (rows = allLabels, extension = Option.None) +|# diff --git a/Compiler/TypeCheck/Ty.fir b/Compiler/TypeCheck/Ty.fir index 143620b7..033abe5b 100644 --- a/Compiler/TypeCheck/Ty.fir +++ b/Compiler/TypeCheck/Ty.fir @@ -51,18 +51,36 @@ type Ty: exn: Option[Ty], ) - ## An anonymous record or variant type or row type. E.g. `(a: Str, ..r)`, - ## `[Err1(Str), ..r]`. - Anonymous( - labels: HashMap[LocalId, Ty], - - ## Row extension. When available, this will be one of: + ## A variant: `[Str, Option[U32], ..r]`. + Variant( + ## Maps type constructors in the variant to their arguments. + ## + ## The arguments may be emtpy, e.g. for `Bool`. ## - ## - `Ty.Var`: a unification variable. - ## - `Ty.Con`: a rigid type variable. + ## This representation means we don't allow the same type constructor + ## with multiple arguments, e.g. `[Vec[U32], Vec[U64]]` is not allowed. + ## + ## I think we could easily support this in generated code, but I'm not + ## sure if it's useful, and this representation is easier to deal with + ## in the type checker. + cons: HashMap[TyDefIdx, Vec[Ty]], + + ## Row extension. When available, this will be one of: `Ty.RVar` or + ## `Ty.UVar`. extension: Option[Ty], - kind: RecordOrVariant, + ## Whether this is a row type. A row type has its own kind `row`. When + ## not a row, the type has kind `*`. + isRow: Bool, + ) + + ## A record: `(a: Str, b: Option[U32], ..r)`. + Record( + labels: HashMap[LocalId, Ty], + + ## Row extension. When available, this will be one of: `Ty.RVar` or + ## `Ty.UVar`. + extension: Option[Ty], ## Whether this is a row type. A row type has its own kind `row`. When ## not a row, the type has kind `*`. @@ -179,40 +197,39 @@ Ty.kind(self) Kind: match self: Ty.Con(..) | Ty.App(..) | Ty.Fun(..): Kind.Star - Ty.QVar(QVar(kind, ..)) | Ty.RVar(RVar(kind, ..)): kind + Ty.QVar(QVar(kind, ..)) + | Ty.RVar(RVar(kind, ..)) + | Ty.UVar(UVar(kind, ..)): kind - Ty.UVar(uvar): uvar.kind + Ty.Variant(isRow, ..): + if isRow: + Kind.Row(RecordOrVariant.Variant) + else: + Kind.Star - Ty.Anonymous(kind, ..): Kind.Row(kind) + Ty.Record(isRow, ..): + if isRow: + Kind.Row(RecordOrVariant.Record) + else: + Kind.Star Ty.unit() Ty: - Ty.Anonymous( + Ty.Record( labels = HashMap.empty(), extension = Option.None, - kind = RecordOrVariant.Record, isRow = Bool.False, ) Ty.emptyVariant() Ty: - Ty.Anonymous( - labels = HashMap.empty(), + Ty.Variant( + cons = HashMap.empty(), extension = Option.None, - kind = RecordOrVariant.Variant, isRow = Bool.False, ) -Ty.unitRow(kind: RecordOrVariant) Ty: - Ty.Anonymous( - labels = HashMap.empty(), - extension = Option.None, - kind, - isRow = Bool.True, - ) - - # ------------------------------------------------------------------------------ # Type schemes and instantiation @@ -279,7 +296,7 @@ Scheme.instantiate( # shadowing, or handle shadowing here. # Maps `QVar`s to unification variables. - let varMap = HashMap[LocalId, Ty].withCapacity(10) + let varMap = HashMap[LocalId, Ty].withCapacity(self.qvars.len()) # Instantiated type parameters, in the same order as `self.qvars`. let instantiations = Vec[UVar].withCapacity(self.qvars.len()) @@ -313,6 +330,32 @@ Scheme.instantiate( (ty = self.ty.substQVars(varMap), vars = instantiations) +Scheme.instantiateWithTys(self, argTys: Vec[Ty], preds: Vec[Pred], loc: Loc,) Ty: + assert(self.qvars.len() == argTys.len()) + + # Maps `QVar`s to unification variables. + let varMap = HashMap[LocalId, Ty].withCapacity(self.qvars.len()) + + for argIdx: U32 in range(u32(0), argTys.len()): + varMap.insert(self.qvars.get(argIdx).id, argTys.get(argIdx)) + + # Generate predicates. + for pred: Pred in self.preds.iter(): + let instantiatedPred = Pred( + trait_ = pred.trait_, + params = + Vec.fromIter( + pred.params.iter().map( + \(param: Ty): param.substQVars(varMap), + ), + ), + loc, + ) + preds.push(instantiatedPred) + + self.ty.substQVars(varMap) + + # ------------------------------------------------------------------------------ # Predicates @@ -393,8 +436,30 @@ Ty.substQVars(self, vars: HashMap[LocalId, Ty]) Ty: exn = exn.map(\(exn: Ty): exn.substQVars(vars)), ) - Ty.Anonymous(labels, extension, kind, isRow): - Ty.Anonymous( + Ty.Variant(cons, extension, isRow): + Ty.Variant( + cons = + HashMap.fromIter( + cons.iter().map( + \(entry: HashMapEntry[TyDefIdx, Vec[Ty]]) (key: TyDefIdx, value: Vec[Ty]): + ( + key = entry.key, + value = + Vec.fromIter( + entry.value.iter().map( + \(ty: Ty) Ty: + ty.substQVars(vars), + ), + ), + ), + ), + ), + extension = extension.map(\(ty: Ty): ty.substQVars(vars)), + isRow, + ) + + Ty.Record(labels, extension, isRow): + Ty.Record( labels = HashMap.fromIter( labels.iter().map( @@ -406,7 +471,6 @@ Ty.substQVars(self, vars: HashMap[LocalId, Ty]) Ty: ), ), extension = extension.map(\(ty: Ty): ty.substQVars(vars)), - kind, isRow, ) @@ -417,83 +481,7 @@ Ty.substQVars(self, vars: HashMap[LocalId, Ty]) Ty: impl ToDoc[Ty]: toDoc(self: Ty) Doc: - match self: - Ty.Con(id): - let args = Doc.break_(0) - args += Doc.grouped( - Doc.str("id =") + Doc.nested(4, Doc.break_(1) + id.toDoc()), - ) - args = args.nest(4).group() + Doc.break_(0) + Doc.char(')') - Doc.str("Ty.Con") + Doc.char('(') + args - Ty.App(conId, args): - let args = Doc.break_(0) - args += Doc.grouped( - Doc.str("conId =") - + Doc.nested(4, Doc.break_(1) + conId.toDoc()), - ) - args += Doc.char(',') + Doc.break_(1) - args += Doc.grouped( - Doc.str("args =") - + Doc.nested(4, Doc.break_(1) + args.toDoc()), - ) - args = args.nest(4).group() + Doc.break_(0) + Doc.char(')') - Doc.str("Ty.App") + Doc.char('(') + args - Ty.QVar(i0): - let args = Doc.break_(0) - args += i0.toDoc() - args = args.nest(4).group() + Doc.break_(0) + Doc.char(')') - Doc.grouped(Doc.str("Ty.QVar") + Doc.char('(') + args) - Ty.UVar(i0): - let args = Doc.break_(0) - args += i0.toDoc() - args = args.nest(4).group() + Doc.break_(0) + Doc.char(')') - Doc.grouped(Doc.str("Ty.UVar") + Doc.char('(') + args) - Ty.RVar(i0): - let args = Doc.break_(0) - args += i0.toDoc() - args = args.nest(4).group() + Doc.break_(0) + Doc.char(')') - Doc.grouped(Doc.str("Ty.RVar") + Doc.char('(') + args) - Ty.Fun(args, ret, exn): - let args = Doc.break_(0) - args += Doc.grouped( - Doc.str("args =") - + Doc.nested(4, Doc.break_(1) + args.toDoc()), - ) - args += Doc.char(',') + Doc.break_(1) - args += Doc.grouped( - Doc.str("ret =") - + Doc.nested(4, Doc.break_(1) + ret.toDoc()), - ) - args += Doc.char(',') + Doc.break_(1) - args += Doc.grouped( - Doc.str("exn =") - + Doc.nested(4, Doc.break_(1) + exn.toDoc()), - ) - args = args.nest(4).group() + Doc.break_(0) + Doc.char(')') - Doc.str("Ty.Fun") + Doc.char('(') + args - Ty.Anonymous(labels, extension, kind, isRow): - let args = Doc.break_(0) - args += Doc.grouped( - Doc.str("labels =") - + Doc.nested(4, Doc.break_(1) + labels.toDoc()), - ) - args += Doc.char(',') + Doc.break_(1) - args += Doc.grouped( - Doc.str("extension =") - + Doc.nested(4, Doc.break_(1) + extension.toDoc()), - ) - args += Doc.char(',') + Doc.break_(1) - args += Doc.grouped( - Doc.str("kind =") - + Doc.nested(4, Doc.break_(1) + kind.toDoc()), - ) - args += Doc.char(',') + Doc.break_(1) - args += Doc.grouped( - Doc.str("isRow =") - + Doc.nested(4, Doc.break_(1) + isRow.toDoc()), - ) - args = args.nest(4).group() + Doc.break_(0) + Doc.char(')') - Doc.str("Ty.Anonymous") + Doc.char('(') + args + panic("TODO") impl ToDoc[QVar]: diff --git a/Compiler/TypeCheck/TyCon.fir b/Compiler/TypeCheck/TyCon.fir index c42fea7c..f43366e1 100644 --- a/Compiler/TypeCheck/TyCon.fir +++ b/Compiler/TypeCheck/TyCon.fir @@ -36,7 +36,7 @@ type TyConDetails: TyConDetails.placeholder() TyConDetails: - TyConDetails.Type(TypeDetails(cons = Vec.empty(), sum = Bool.False)) + TyConDetails.Type(TypeDetails(cons = HashMap.empty(), sum = Bool.False)) #[derive(ToDoc)] @@ -65,8 +65,8 @@ type TraitMethod( #[derive(ToDoc)] type TypeDetails( - ## Names of value constructors of the type. - cons: Vec[Str], + ## Value constructors of the type. + cons: HashMap[Str, Scheme], ## Whether the type is a sum type. ## diff --git a/Compiler/TypeCheck/Unification.fir b/Compiler/TypeCheck/Unification.fir index 1c7fda51..277bb89e 100644 --- a/Compiler/TypeCheck/Unification.fir +++ b/Compiler/TypeCheck/Unification.fir @@ -145,6 +145,7 @@ unify(ty1: Ty, ty2: Ty, varGen: UVarGen, level: U32, loc: Loc) / TypeError: unify(ret1, ret2, varGen, level, loc) + #| ( ty1 = Ty.Anonymous( @@ -277,6 +278,7 @@ unify(ty1: Ty, ty2: Ty, varGen: UVarGen, level: U32, loc: Loc) / TypeError: (ext1 = Option.Some(ext1), ext2 = Option.Some(ext2)): unify(ext1, ext2, varGen, level, loc) + |# _: throw( @@ -409,6 +411,7 @@ unifyOneWay(ty1: Ty, ty2: Ty, varGen: UVarGen, level: U32, loc: Loc) Bool: unifyOneWay(ret1, ret2, varGen, level, loc) + #| ( ty1 = Ty.Anonymous( @@ -493,6 +496,7 @@ unifyOneWay(ty1: Ty, ty2: Ty, varGen: UVarGen, level: U32, loc: Loc) Bool: (ext1 = Option.Some(ext1), ext2 = Option.Some(ext2)): unifyOneWay(ext1, ext2, varGen, level, loc) + |# _: Bool.False @@ -505,6 +509,7 @@ linkVar(var_: UVar, ty: Ty): var_.setLink(ty) +#| linkExtension( kind: RecordOrVariant, extraLabels: HashSet[LocalId], @@ -532,7 +537,7 @@ linkExtension( uvar.setLink(newExtensionTy) newExtensionVar - +|# Ty.pruneLevel(self, maxLevel: U32): match self: @@ -563,7 +568,15 @@ Ty.pruneLevel(self, maxLevel: U32): if exn is Option.Some(exn): exn.pruneLevel(maxLevel) - Ty.Anonymous(labels, extension, ..): + Ty.Variant(cons, extension, ..): + for tys: Vec[Ty] in cons.values(): + for ty: Ty in tys.iter(): + ty.pruneLevel(maxLevel) + + if extension is Option.Some(extension): + extension.pruneLevel(maxLevel) + + Ty.Record(labels, extension, ..): for ty: Ty in labels.values(): ty.pruneLevel(maxLevel)