diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bb493e08..3d836e46 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,16 +6,18 @@ add_executable(verona subtype.cc passes/modules.cc passes/structure.cc - passes/memberconflict.cc + passes/conditionals.cc + passes/reference.cc passes/typenames.cc passes/typeview.cc passes/typefunc.cc passes/typealg.cc passes/typeflat.cc passes/typevalid.cc + passes/typereference.cc passes/codereuse.cc - passes/conditionals.cc - passes/reference.cc + passes/memberconflict.cc + passes/resetimplicit.cc passes/reverseapp.cc passes/application.cc passes/assignlhs.cc @@ -33,6 +35,7 @@ add_executable(verona passes/defbeforeuse.cc passes/drop.cc passes/validtypeargs.cc + passes/typeinfer.cc ) target_precompile_headers(verona diff --git a/src/btype.h b/src/btype.h index 52f59ed2..2b6f37fc 100644 --- a/src/btype.h +++ b/src/btype.h @@ -23,11 +23,11 @@ namespace verona while (true) { - if (node->type().in({Type, TypePred})) + if (node->in({Type, TypePred})) { node = node / Type; } - else if (node->type().in({FQType, FQFunction})) + else if (node->in({FQType, FQFunction})) { auto lookup = resolve_fq(node); @@ -45,7 +45,7 @@ namespace verona if (set.contains(node)) return; } - else if (node->type() == TypeParam) + else if (node == TypeParam) { set.insert(node); auto it = bindings.find(node); @@ -85,16 +85,16 @@ namespace verona return make(t, bindings); } - Btype field(const Token& f) - { - return make(node / f, bindings); - } - const Token& type() const { return node->type(); } + bool in(const std::initializer_list& list) const + { + return node->in(list); + } + void str(std::ostream& out, size_t level) { out << indent(level) << "btype: {" << std::endl; @@ -102,7 +102,7 @@ namespace verona // Print the node. out << indent(level + 1) << "node: {" << std::endl; - if (node->type().in({Class, TypeAlias, Function})) + if (node->in({Class, TypeAlias, Function})) { out << indent(level + 2) << node->type().str() << " " << (node / Ident)->location().view(); @@ -136,6 +136,16 @@ namespace verona return BtypeDef::make(t, {}); } + inline Btype operator/(Btype& b, const Token& f) + { + return b->make(b->node / f); + } + + inline bool operator==(const Btype& b, const Token& type) + { + return b->type() == type; + } + inline std::ostream& operator<<(std::ostream& out, const Btype& b) { b->str(out, 0); diff --git a/src/lang.cc b/src/lang.cc index 94836d72..35056d7f 100644 --- a/src/lang.cc +++ b/src/lang.cc @@ -13,9 +13,19 @@ namespace verona return Error << (ErrorMsg ^ msg) << node; } + Node typevar(Location loc) + { + return Type << (TypeVar ^ loc); + } + + Node typevar(Node& node) + { + return typevar(node->fresh(l_typevar)); + } + Node typevar(Match& _) { - return Type << (TypeVar ^ _.fresh(l_typevar)); + return typevar(_.fresh(l_typevar)); } Node typevar(Match& _, const Token& t) @@ -59,37 +69,83 @@ namespace verona << (TypeClassName << (Ident ^ l_builtin) << TypeArgs); } + static Node builtin_type(const Location& name, Node ta = TypeArgs) + { + return FQType << builtin_path() << (TypeClassName << (Ident ^ name) << ta); + } + + static Node call0(Node type, const Location& loc) + { + return call(FQFunction << type << selector(loc)); + } + + static Node create0(Node type) + { + return call0(type, l_create); + } + Node nonlocal(Match& _) { // Pin the type argument to a specific type variable. static Location l_nonlocal("nonlocal"); - return FQType << builtin_path() - << (TypeClassName << (Ident ^ l_nonlocal) - << (TypeArgs << typevar(_))); + return builtin_type(l_nonlocal, TypeArgs << typevar(_)); } Node unittype() { static Location l_unit("unit"); - return FQType << builtin_path() - << (TypeClassName << (Ident ^ l_unit) << TypeArgs); + return builtin_type(l_unit); } Node unit() { - return call(FQFunction << unittype() << selector(l_create)); + return create0(unittype()); + } + + Node booltype() + { + static Location l_bool("Bool"); + return builtin_type(l_bool); + } + + Node booltrue() + { + static Location l_true("make_true"); + return call0(booltype(), l_true); + } + + Node boolfalse() + { + static Location l_false("make_false"); + return call0(booltype(), l_false); } Node celltype() { - static Location l_cell("cell"); - return FQType << builtin_path() - << (TypeClassName << (Ident ^ l_cell) << TypeArgs); + static Location l_cell("Cell"); + return builtin_type(l_cell); } Node cell() { - return call(FQFunction << celltype() << selector(l_create)); + return create0(celltype()); + } + + Node reftype(Node t) + { + static Location l_ref("Ref"); + return builtin_type(l_ref, TypeArgs << -t); + } + + Node tuple_to_args(Node n) + { + assert(n == Tuple); + if (n->size() == 0) + return Unit; + else if (n->size() == 1) + return n->front(); + else + return n; } Node selector(Node name, Node ta) @@ -109,119 +165,101 @@ namespace verona { // `op` must already be in the AST in order to resolve the FQFunction. // If not, it won't be treated as an LLVM call. - if (op->type() != FQFunction) + if (op != FQFunction) return false; auto l = resolve_fq(op); - return l.def && (l.def->type() == Function) && - ((l.def / LLVMFuncType)->type() == LLVMFuncType); + return l.def && (l.def == Function) && + ((l.def / LLVMFuncType) == LLVMFuncType); } static Node arg(Node args, Node arg) { if (arg) { - if (arg->type() == Tuple) + if (arg == Tuple) args->push_back({arg->begin(), arg->end()}); - else if (arg->type() == Expr) + else if (arg == Expr) args << arg; - else if (arg->type() != Unit) + else if (arg != Unit) args << (Expr << arg); } return args; } - Node call(Node op, Node lhs, Node rhs, bool post_nlr) + Node call(Node op, Node lhs, Node rhs) { - assert(op->type().in({FQFunction, Selector})); + assert(op->in({FQFunction, Selector})); auto args = arg(arg(Args, lhs), rhs); auto arity = Int ^ std::to_string(args->size()); - if (op->type() == FQFunction) + if (op == FQFunction) (op / Selector / Int) = arity; else (op / Int) = arity; - auto ret = Call << op << args; - - if (!post_nlr) - ret = NLRCheck << Explicit << ret; - - return ret; + return NLRCheck << (Call << op << args); } Node call_lhs(Node call) { - assert(call->type() == Call); + assert(call == Call); auto f = call / Selector; - if (f->type() == FQFunction) + if (f == FQFunction) f = f / Selector; (f / Ref) = Lhs; return call; } - Node load(Node arg, bool post_nlr) + Node load(Node arg) { static Location l_load("load"); - return call(selector(l_load), arg, {}, post_nlr); + return call(selector(l_load), arg); } bool is_implicit(Node n) { auto f = n->parent(Function); - return f && ((f / Implicit)->type() == Implicit); + return f && ((f / Implicit) == Implicit); } static Token handed(Node& node) { - assert(node->type().in({FieldLet, FieldVar, Function})); + assert(node->in({FieldLet, FieldVar, Function})); // Return Op to mean both. - if (node->type() == FieldVar) + if (node == FieldVar) return Op; - else if (node->type() == FieldLet) + else if (node == FieldLet) return Lhs; else return (node / Ref)->type(); } - static std::pair arity(Node& node) + static size_t arity(Node& node) { - assert(node->type().in({FieldLet, FieldVar, Function})); - - if (node->type() != Function) - return {1, 1}; - - auto params = node / Params; - auto arity_hi = params->size(); - auto arity_lo = arity_hi; - - for (auto& param : *params) - { - if ((param / Default)->type() != DontCare) - arity_lo--; - } - - return {arity_lo, arity_hi}; + assert(node->in({FieldLet, FieldVar, Function})); + return (node == Function) ? (node / Params)->size() : 1; } bool conflict(Node& a, Node& b) { - // Check for handedness overlap. + assert(a->in({FieldLet, FieldVar, Function})); + assert(b->in({FieldLet, FieldVar, Function})); + + // Check for handedness conflict. auto a_hand = handed(a); auto b_hand = handed(b); if ((a_hand != b_hand) && (a_hand != Op) && (b_hand != Op)) return false; - // Check for arity overlap. - auto [a_lo, a_hi] = arity(a); - auto [b_lo, b_hi] = arity(b); - return (b_hi >= a_lo) && (a_hi >= b_lo); + // Check for arity conflict. + return arity(a) == arity(b); } Options& options() @@ -240,33 +278,36 @@ namespace verona { {"modules", modules(), wfPassModules}, {"structure", structure(), wfPassStructure}, + {"reference", reference(), wfPassReference}, + {"conditionals", conditionals(), wfPassConditionals}, + {"lambda", lambda(), wfPassLambda}, + {"autocreate", autocreate(), wfPassLambda}, + {"defaultargs", defaultargs(), wfPassDefaultArgs}, {"typenames", typenames(), wfPassTypeNames}, {"typeview", typeview(), wfPassTypeView}, {"typefunc", typefunc(), wfPassTypeFunc}, {"typealg", typealg(), wfPassTypeAlg}, {"typeflat", typeflat(), wfPassTypeFlat}, {"typevalid", typevalid(), wfPassTypeFlat}, - {"reference", reference(), wfPassReference}, - {"codereuse", codereuse(), wfPassReference}, - {"memberconflict", memberconflict(), wfPassReference}, - {"conditionals", conditionals(), wfPassConditionals}, + {"typereference", typereference(), wfPassTypeReference}, + {"codereuse", codereuse(), wfPassTypeReference}, + {"memberconflict", memberconflict(), wfPassTypeReference}, + {"resetimplicit", resetimplicit(), wfPassResetImplicit}, {"reverseapp", reverseapp(), wfPassReverseApp}, {"application", application(), wfPassApplication}, {"assignlhs", assignlhs(), wfPassAssignLHS}, {"localvar", localvar(), wfPassLocalVar}, {"assignment", assignment(), wfPassAssignment}, - {"lambda", lambda(), wfPassLambda}, {"autofields", autofields(), wfPassAutoFields}, {"autorhs", autorhs(), wfPassAutoFields}, - {"autocreate", autocreate(), wfPassAutoCreate}, - {"defaultargs", defaultargs(), wfPassDefaultArgs}, - {"partialapp", partialapp(), wfPassDefaultArgs}, - {"traitisect", traitisect(), wfPassDefaultArgs}, + {"partialapp", partialapp(), wfPassAutoFields}, + {"traitisect", traitisect(), wfPassAutoFields}, {"nlrcheck", nlrcheck(), wfPassNLRCheck}, {"anf", anf(), wfPassANF}, {"defbeforeuse", defbeforeuse(), wfPassANF}, {"drop", drop(), wfPassDrop}, {"validtypeargs", validtypeargs(), wfPassDrop}, + // {"typeinfer", typeinfer(), wfPassDrop}, }); return d; diff --git a/src/lang.h b/src/lang.h index 588a6ceb..d66f2afb 100644 --- a/src/lang.h +++ b/src/lang.h @@ -23,8 +23,10 @@ namespace verona inline const auto DoubleColon = TokenDef("doublecolon"); inline const auto TripleColon = TokenDef("triplecolon"); inline const auto Arrow = TokenDef("arrow"); - inline const auto Bool = TokenDef("bool", flag::print); + inline const auto True = TokenDef("true"); + inline const auto False = TokenDef("false"); inline const auto Hex = TokenDef("hex", flag::print); + inline const auto Oct = TokenDef("oct", flag::print); inline const auto Bin = TokenDef("bin", flag::print); inline const auto Int = TokenDef("int", flag::print); inline const auto HexFloat = TokenDef("hexfloat", flag::print); @@ -74,8 +76,7 @@ namespace verona TokenDef("valueparam", flag::lookup | flag::lookdown | flag::shadowing); inline const auto Params = TokenDef("params"); inline const auto Param = TokenDef("param", flag::lookup | flag::shadowing); - inline const auto Block = - TokenDef("block", flag::symtab | flag::defbeforeuse); + inline const auto Block = TokenDef("block"); // Type structure. inline const auto Type = TokenDef("type"); @@ -138,43 +139,74 @@ namespace verona // Rewrite identifiers. inline const auto Implicit = TokenDef("implicit"); inline const auto Explicit = TokenDef("explicit"); - inline const auto Lhs = TokenDef("Lhs"); - inline const auto Rhs = TokenDef("Rhs"); - inline const auto Op = TokenDef("Op"); + inline const auto LambdaFunc = TokenDef("lambdafunc"); + inline const auto Lhs = TokenDef("lhs"); + inline const auto Rhs = TokenDef("rhs"); + inline const auto Op = TokenDef("op"); // Sythetic locations. inline const auto l_typevar = Location("typevar"); inline const auto l_param = Location("param"); inline const auto l_trait = Location("trait"); - inline const auto l_class = Location("class"); + inline const auto l_objlit = Location("objlit"); + inline const auto l_lambda = Location("lambda"); inline const auto l_self = Location("self"); inline const auto l_new = Location("new"); inline const auto l_apply = Location("apply"); inline const auto l_create = Location("create"); // Helper patterns. - inline const auto IsImplicit = T(Implicit) / T(Explicit); - inline const auto Hand = T(Lhs) / T(Rhs); - inline const auto TypeStruct = In(Type) / In(TypeList) / In(TypeTuple) / - In(TypeView) / In(TypeUnion) / In(TypeIsect) / In(TypeSubtype); - inline const auto TypeCaps = T(Iso) / T(Mut) / T(Imm); - inline const auto Literal = T(String) / T(Escaped) / T(Char) / T(Bool) / - T(Hex) / T(Bin) / T(Int) / T(Float) / T(HexFloat) / T(LLVM); - inline const auto TypeElem = T(Type) / TypeCaps / T(FQType) / T(Trait) / - T(TypeTuple) / T(Self) / T(TypeList) / T(TypeView) / T(TypeIsect) / - T(TypeUnion) / T(TypeVar) / T(Package) / T(TypeSubtype) / T(TypeTrue) / - T(TypeFalse); - inline const auto Object0 = Literal / T(RefVar) / T(RefVarLHS) / T(RefLet) / - T(Unit) / T(Tuple) / T(Lambda) / T(Call) / T(NLRCheck) / T(Assign) / - T(Expr) / T(ExprSeq) / T(DontCare) / T(Conditional) / T(TypeTest) / T(Cast); + inline const auto IsImplicit = T(Implicit, Explicit, LambdaFunc); + inline const auto Hand = T(Lhs, Rhs); + inline const auto TypeStruct = + In(Type, TypeList, TypeTuple, TypeView, TypeUnion, TypeIsect, TypeSubtype); + inline const auto TypeCaps = T(Iso, Mut, Imm); + inline const auto Literal = + T(String, Escaped, Char, Hex, Oct, Bin, Int, Float, HexFloat, LLVM); + + inline const auto TypeElem = TypeCaps / + T(Type, + FQType, + Trait, + TypeTuple, + Self, + TypeList, + TypeView, + TypeIsect, + TypeUnion, + TypeVar, + Package, + TypeSubtype, + TypeTrue, + TypeFalse); + + inline const auto Object0 = Literal / + T(RefVar, + RefVarLHS, + RefLet, + Unit, + Tuple, + Lambda, + Call, + NLRCheck, + Assign, + Expr, + ExprSeq, + DontCare, + Conditional, + TypeTest, + Cast); + inline const auto Object = Object0 / (T(TypeAssert) << (Object0 * T(Type))); - inline const auto Operator = T(FQFunction) / T(Selector); + inline const auto Operator = T(FQFunction, Selector); inline const auto RhsCall = T(Call) << ((T(Selector) << T(Rhs)) / (T(FQFunction) << (T(FQType) * (T(Selector) << T(Rhs))))); // Helper functions for generating AST fragments. Node err(Node node, const std::string& msg); + Node typevar(Location loc); + Node typevar(Node& node); Node typevar(Match& _); Node typevar(Match& _, const Token& t); Node inherit(); @@ -184,13 +216,18 @@ namespace verona Node nonlocal(Match& _); Node unittype(); Node unit(); + Node booltype(); + Node booltrue(); + Node boolfalse(); Node cell(); + Node reftype(Node t); + Node tuple_to_args(Node n); Node selector(Node name, Node ta = TypeArgs); Node selector(Location name, Node ta = TypeArgs); bool is_llvm_call(Node op); - Node call(Node op, Node lhs = {}, Node rhs = {}, bool post_nlr = false); + Node call(Node op, Node lhs = {}, Node rhs = {}); Node call_lhs(Node call); - Node load(Node arg, bool post_nlr = false); + Node load(Node arg); bool is_implicit(Node n); bool conflict(Node& a, Node& b); @@ -198,7 +235,8 @@ namespace verona Parse parser(); PassDef modules(); PassDef structure(); - PassDef memberconflict(); + PassDef conditionals(); + PassDef reference(); PassDef typenames(); PassDef typeview(); PassDef typefunc(); @@ -206,8 +244,9 @@ namespace verona PassDef typeflat(); PassDef typevalid(); PassDef codereuse(); - PassDef conditionals(); - PassDef reference(); + PassDef memberconflict(); + PassDef resetimplicit(); + PassDef typereference(); PassDef reverseapp(); PassDef application(); PassDef assignlhs(); @@ -226,6 +265,7 @@ namespace verona PassDef drop(); PassDef namearity(); PassDef validtypeargs(); + PassDef typeinfer(); struct Options : public trieste::Options { diff --git a/src/lookup.cc b/src/lookup.cc index 23688db3..ab39d4fe 100644 --- a/src/lookup.cc +++ b/src/lookup.cc @@ -9,9 +9,39 @@ namespace verona { + size_t Lookup::sub(Node& node) + { + // Substitutes inside of `node`, but not `node` itself. + size_t changes = 0; + + for (auto child : *node) + { + while ((child == FQType) && ((child / Type) == TypeParamName)) + { + auto l = resolve_fq(child); + auto it = bindings.find(l.def); + + if (it == bindings.end()) + break; + + auto bind = clone(it->second); + + if (bind == Type) + bind = bind / Type; + + node->replace(child, bind); + child = bind; + } + + changes += sub(child); + } + + return changes; + } + static void apply_typeargs(Lookup& lookup, Node ta) { - if (!lookup.def->type().in({Class, TypeAlias, Function})) + if (!lookup.def->in({Class, TypeAlias, Function})) return; auto tp = lookup.def / TypeParams; @@ -39,23 +69,29 @@ namespace verona tp->begin() + n, tp->end(), std::inserter(lookup.bindings, lookup.bindings.end()), - [](auto param) { - return std::make_pair( - param, Type << (TypeVar ^ param->fresh(l_typevar))); - }); + [](auto param) { return std::make_pair(param, typevar(param)); }); } Lookups lookup(Node id, Node ta) { - assert(id->type().in({Ident, Symbol})); - assert(!ta || (ta->type() == TypeArgs)); + assert(id->in({Ident, Symbol, Self})); + assert(!ta || (ta == TypeArgs)); + Lookups result; + + if (id == Self) + { + auto def = id->parent({Class, Trait}); + Lookup l(def); + apply_typeargs(l, ta); + result.emplace(l); + return result; + } auto defs = id->lookup(); - Lookups result; for (auto& def : defs) { - if (def->type() == Use) + if (def == Use) { // Expand Use nodes by looking down into the target type. if (def->precedes(id)) @@ -89,7 +125,7 @@ namespace verona if (!inserted.second) return {}; - if (lookup.def->type().in({Class, Trait, Function})) + if (lookup.def->in({Class, Trait, Function})) { // Return all lookdowns in the found class, trait, or function. Lookups result; @@ -107,45 +143,43 @@ namespace verona return result; } - else if (lookup.def->type() == TypeAlias) + else if (lookup.def == TypeAlias) { // Replace the def with our type alias and try again. lookup.def = lookup.def / Type; } - else if (lookup.def->type() == TypeParam) + else if (lookup.def == TypeParam) { // Replace the typeparam with the bound typearg and try again. auto it = lookup.bindings.find(lookup.def); // Except in testing, there should always be a binding. If it's bound // to itself, then we're done. - if ( - (it == lookup.bindings.end()) || - (it->second->type() == TypeParamBind)) + if ((it == lookup.bindings.end()) || (it->second == TypeParamBind)) return {}; lookup.def = it->second; } // The remainder of cases arise from a Use, a TypeAlias, or a TypeParam. // They will all result in some number of name resolutions. - else if (lookup.def->type() == Type) + else if (lookup.def == Type) { // Replace the def with the content of the type and try again. // Use `front()` instead of `def / Type` to allow looking up in `use` // directives before Type is no longer a sequence. lookup.def = lookup.def->front(); } - else if (lookup.def->type().in({FQType, FQFunction})) + else if (lookup.def->in({FQType, FQFunction})) { // Resolve the name and try again. lookup = resolve_fq(lookup.def); } - else if (lookup.def->type() == TypeView) + else if (lookup.def == TypeView) { // Replace the def with the rhs of the view and try again. lookup.def = lookup.def->back(); } - else if (lookup.def->type() == TypeIsect) + else if (lookup.def == TypeIsect) { // Return everything we find. Lookups result; @@ -159,12 +193,12 @@ namespace verona return result; } - else if (lookup.def->type() == TypeUnion) + else if (lookup.def == TypeUnion) { // TODO: return only things that are identical in all disjunctions return {}; } - else if (lookup.def->type().in({TypeList, TypeTuple, TypeVar})) + else if (lookup.def->in({TypeList, TypeTuple, TypeVar})) { // Nothing to do here. return {}; @@ -180,7 +214,7 @@ namespace verona bool lookup_type(Node id, std::initializer_list t) { auto defs = lookup(id, {}); - return (defs.size() == 1) && defs.begin()->def->type().in(t); + return (defs.size() == 1) && defs.begin()->def->in(t); } bool lookup_type(const NodeRange& n, std::initializer_list t) @@ -190,13 +224,12 @@ namespace verona static bool resolve_selector(Lookup& p, Node n) { - assert(n->type() == Selector); + assert(n == Selector); if (!p.def) return false; auto hand = (n / Ref)->type(); - auto id = n / Ident; auto defs = p.def->lookdown(id->location()); @@ -207,7 +240,7 @@ namespace verona for (auto& def : defs) { if ( - (def->type() == Function) && ((def / Ref)->type() == hand) && + (def == Function) && ((def / Ref) == hand) && ((def / Params)->size() == arity)) { p = p.make(def); @@ -221,21 +254,21 @@ namespace verona static bool resolve_typename(Lookup& p, Node n) { - assert(n->type().in( - {TypeClassName, TypeAliasName, TypeParamName, TypeTraitName})); + assert(n->in({TypeClassName, TypeAliasName, TypeParamName, TypeTraitName})); + auto def = p.def; - if (!p.def) + if (!def) return false; auto id = n / Ident; - auto defs = p.def->lookdown(id->location()); + auto defs = def->lookdown(id->location()); if (defs.size() != 1) return false; p = p.make(*defs.begin()); - if (n->type().in({TypeClassName, TypeAliasName})) + if (n->in({TypeClassName, TypeAliasName})) apply_typeargs(p, n / TypeArgs); return true; @@ -243,18 +276,18 @@ namespace verona static Lookup resolve_fqtype(Node fq) { - assert(fq->type() == FQType); + assert(fq == FQType); Lookup p(fq->parent(Top)); auto path = fq / TypePath; for (auto& n : *path) { - if (n->type() == Selector) + if (n == Selector) { if (!resolve_selector(p, n)) return {}; } - else if (n->type().in( + else if (n->in( {TypeClassName, TypeAliasName, TypeParamName, TypeTraitName})) { if (!resolve_typename(p, n)) @@ -270,10 +303,10 @@ namespace verona Lookup resolve_fq(Node fq) { - if (fq->type() == FQType) + if (fq == FQType) return resolve_fqtype(fq); - assert(fq->type() == FQFunction); + assert(fq == FQFunction); auto p = resolve_fqtype(fq / FQType); if (!resolve_selector(p, fq / Selector)) @@ -294,7 +327,7 @@ namespace verona if (it != lookup.bindings.end()) ta << clone(it->second); else if (fresh) - ta << (Type << (TypeVar ^ node->fresh(l_typevar))); + ta << typevar(node); else ta << TypeParamBind; } @@ -304,7 +337,7 @@ namespace verona static Node make_fq(Lookup& lookup, bool fresh) { - if (!lookup.def->type().in({Class, TypeAlias, TypeParam, Trait, Function})) + if (!lookup.def->in({Class, TypeAlias, TypeParam, Trait, Function})) return lookup.def; Node path = TypePath; @@ -312,27 +345,27 @@ namespace verona while (node) { - if (node->type() == Class) + if (node == Class) { path << (TypeClassName << clone(node / Ident) << make_typeargs(node, lookup, fresh)); } - else if (node->type() == TypeAlias) + else if (node == TypeAlias) { path << (TypeAliasName << clone(node / Ident) << make_typeargs(node, lookup, fresh)); } - else if (node->type() == TypeParam) + else if (node == TypeParam) { path << (TypeParamName << clone(node / Ident)); } - else if (node->type() == Trait) + else if (node == Trait) { path << (TypeTraitName << clone(node / Ident)); } - else if (node->type() == Function) + else if (node == Function) { auto arity = std::to_string((node / Params)->size()); @@ -347,8 +380,7 @@ namespace verona std::reverse(path->begin(), path->end()); node = path->pop_back(); - if (node->type().in( - {TypeClassName, TypeAliasName, TypeParamName, TypeTraitName})) + if (node->in({TypeClassName, TypeAliasName, TypeParamName, TypeTraitName})) return FQType << path << node; assert(!path->empty()); @@ -364,7 +396,7 @@ namespace verona Node local_fq(Node node) { // Build an FQType for the local type. - if (!node->type().in({Class, TypeAlias, TypeParam, Trait, Function})) + if (!node->in({Class, TypeAlias, TypeParam, Trait, Function})) node = node->parent({Class, TypeAlias, TypeParam, Trait, Function}); Lookup l(node); @@ -373,14 +405,26 @@ namespace verona Node append_fq(Node fq, Node node) { - assert(fq->type() == FQType); + assert(fq->type().in({FQType, FQFunction})); + assert(node->in( + {TypeClassName, TypeAliasName, TypeParamName, TypeTraitName, Selector})); - if (node->type() == Selector) + if (fq == FQFunction) + { + // FQFunction + Selector = not allowed + assert(node != Selector); + + // FQFunction + Type = FQType + return FQType << (clone(fq / FQType / TypePath) + << clone(fq / FQType / Type) << clone(fq / Selector)) + << node; + } + + // FQType + Selector = FQFunction + if (node == Selector) return FQFunction << clone(fq) << node; - assert(node->type().in( - {TypeClassName, TypeAliasName, TypeParamName, TypeTraitName})); - + // FQType + Type = FQType return FQType << (clone(fq / TypePath) << clone(fq / Type)) << node; } } diff --git a/src/lookup.h b/src/lookup.h index 0850b340..3f034e4a 100644 --- a/src/lookup.h +++ b/src/lookup.h @@ -27,6 +27,8 @@ namespace verona { return (def < other.def) && (bindings < other.bindings); } + + size_t sub(Node& node); }; using Lookups = std::set; diff --git a/src/parse.cc b/src/parse.cc index 005afd8e..904633c9 100644 --- a/src/parse.cc +++ b/src/parse.cc @@ -173,24 +173,28 @@ namespace verona }, // Bool. - "(?:true|false)\\b" >> [](auto& m) { m.add(Bool); }, + "true\\b" >> [](auto& m) { m.add(True); }, + "false\\b" >> [](auto& m) { m.add(False); }, // Hex float. - "0x[[:xdigit:]]+\\.[[:xdigit:]]+(?:p[+-][[:digit:]]+)?\\b" >> + "0x[_[:xdigit:]]+\\.[_[:xdigit:]]+(?:p[+-][_[:digit:]]+)?\\b" >> [](auto& m) { m.add(HexFloat); }, - // Hex. - "0x[_[:xdigit:]]+\\b" >> [](auto& m) { m.add(Hex); }, + // Float. + "[[:digit:]][_[:digit:]]*\\.[_[:digit:]]+(?:e[+-]?[_[:digit:]]+)?\\b" >> + [](auto& m) { m.add(Float); }, // Bin. "0b[_01]+\\b" >> [](auto& m) { m.add(Bin); }, - // Float. - "[[:digit:]]+\\.[[:digit:]]+(?:e[+-]?[[:digit:]]+)?\\b" >> - [](auto& m) { m.add(Float); }, + // Oct. + "0o[_01234567]+\\b" >> [](auto& m) { m.add(Oct); }, + + // Hex. + "0x[_[:xdigit:]]+\\b" >> [](auto& m) { m.add(Hex); }, // Int. - "[[:digit:]]+\\b" >> [](auto& m) { m.add(Int); }, + "[[:digit:]][_[:digit:]]*\\b" >> [](auto& m) { m.add(Int); }, // Escaped string. "\"((?:\\\"|[^\"])*?)\"" >> [](auto& m) { m.add(Escaped, 1); }, @@ -334,7 +338,6 @@ namespace verona }); p.gen({ - Bool >> [](auto& rnd) { return rnd() % 2 ? "true" : "false"; }, Int >> [](auto& rnd) { return std::to_string(rnd()); }, Hex >> [](auto& rnd) { return fmt::format("{:#x}", rnd()); }, Bin >> [](auto& rnd) { return fmt::format("{:#b}", rnd()); }, diff --git a/src/passes/anf.cc b/src/passes/anf.cc index 2bcce884..97119a99 100644 --- a/src/passes/anf.cc +++ b/src/passes/anf.cc @@ -4,8 +4,8 @@ namespace verona { - inline const auto Liftable = T(Tuple) / T(Call) / T(Conditional) / - T(FieldRef) / T(TypeTest) / T(Cast) / T(Selector) / Literal; + inline const auto Liftable = + Literal / T(Tuple, Call, Conditional, FieldRef, TypeTest, Cast, Selector); PassDef anf() { @@ -22,7 +22,7 @@ namespace verona }, // Lift RefLet and Return. - T(Expr) << (T(RefLet) / T(Return))[Op] >> [](Match& _) { return _(Op); }, + T(Expr) << T(RefLet, Return)[Op] >> [](Match& _) { return _(Op); }, // Lift LLVM literals that are at the block level. In(Block) * (T(Expr) << T(LLVM)[LLVM]) >> diff --git a/src/passes/application.cc b/src/passes/application.cc index 50928239..fb04f9b4 100644 --- a/src/passes/application.cc +++ b/src/passes/application.cc @@ -1,6 +1,7 @@ // Copyright Microsoft and Project Verona Contributors. // SPDX-License-Identifier: MIT #include "../lang.h" +#include "../lookup.h" namespace verona { @@ -13,16 +14,13 @@ namespace verona T(Ref) * T(RefVar)[RefVar] >> [](Match& _) { return RefVarLHS << *_[RefVar]; }, - T(Ref) * (T(NLRCheck) << (IsImplicit[Implicit] * RhsCall[Call])) >> - [](Match& _) { return NLRCheck << _(Implicit) << call_lhs(_(Call)); }, + T(Ref) * (T(NLRCheck) << RhsCall[Call]) >> + [](Match& _) { return NLRCheck << call_lhs(_(Call)); }, // Try expressions. - T(Try) * (T(NLRCheck) << (IsImplicit * T(Call)[Call])) >> + T(Try) * (T(NLRCheck) << T(Call)[Call]) >> [](Match& _) { return _(Call); }, - T(Try) * T(Lambda)[Lambda] >> - [](Match& _) { return call(selector(l_apply), _(Lambda)); }, - // Adjacency: application. In(Expr) * Object[Lhs] * Object[Rhs] >> [](Match& _) { return call(selector(l_apply), _(Lhs), _(Rhs)); }, @@ -44,7 +42,7 @@ namespace verona [](Match& _) { return TupleFlatten << (Expr << _(Lhs)); }, // Use `_` (DontCare) for partial application of arbitrary arguments. - T(Call) + T(Call)[Call] << (Operator[Op] * (T(Args) << ((T(Expr) << !T(DontCare))++ * @@ -53,23 +51,34 @@ namespace verona (T(TypeAssert) << (T(DontCare) * T(Type))))) * T(Expr)++))[Args]) >> [](Match& _) { - Node params = Params; + // Create the anonymous type name. + bool in_nlrcheck = _(Call)->parent() == NLRCheck; + auto class_id = _.fresh(l_lambda); + + // Build an FQType for the anonymous type. + auto fq = append_fq( + local_fq(_(Call)->parent(Function)), + TypeClassName << (Ident ^ class_id) << TypeArgs); + + // Start with a Self parameter. + Node params = Params + << (Param << (Ident ^ _.fresh(l_self)) << (Type << Self)); Node args = Tuple; for (auto& arg : *_(Args)) { auto expr = arg->front(); - if (expr->type() == DontCare) + if (expr == DontCare) { auto id = _.fresh(l_param); - params << (Param << (Ident ^ id) << typevar(_) << DontCare); + params << (Param << (Ident ^ id) << typevar(_)); args << (Expr << (RefLet << (Ident ^ id))); } - else if (expr->type() == TypeAssert) + else if (expr == TypeAssert) { auto id = _.fresh(l_param); - params << (Param << (Ident ^ id) << (expr / Type) << DontCare); + params << (Param << (Ident ^ id) << (expr / Type)); args << (Expr << (RefLet << (Ident ^ id))); } else @@ -78,23 +87,42 @@ namespace verona } } - return Lambda << TypeParams << params << typevar(_) << typepred() - << (Block << (Expr << call(_(Op), args))); + // Add the create and apply functions to the anonymous type. + auto create_func = Function + << Implicit << Rhs << (Ident ^ l_create) << TypeParams << Params + << typevar(_) << DontCare << typepred() + << (Block << (Expr << call(append_fq(fq, selector(l_new))))); + + auto apply_func = Function << LambdaFunc << Rhs << (Ident ^ l_apply) + << TypeParams << params << typevar(_) + << DontCare << typepred() + << (Block << (Expr << call(_(Op), args))); + + auto classdef = Class << (Ident ^ class_id) << TypeParams + << (Inherit << DontCare) << typepred() + << (ClassBody << create_func << apply_func); + + auto create = call(append_fq(fq, selector(l_create))); + + if (in_nlrcheck) + create = create / Call; + + return Seq << (Lift << Block << classdef) << create; }, - // Remove the NLRCheck from a partial application. - T(NLRCheck) << (IsImplicit * T(Lambda)[Lambda] * End) >> - [](Match& _) { return _(Lambda); }, - + // Remaining DontCare are discarded bindings. In(Expr) * T(DontCare) >> - [](Match& _) { - // Remaining DontCare are discarded bindings. - return Let << (Ident ^ _.fresh()); - }, + [](Match& _) { return Let << (Ident ^ _.fresh()); }, - // Turn remaining uses of Unit into std::builtin::Unit::create() + // Turn Unit into std::builtin::Unit::create() T(Unit) >> [](Match&) { return unit(); }, + // Turn True into std::builtin::Bool::make_true() + T(True) >> [](Match&) { return booltrue(); }, + + // Turn False into std::builtin::Bool::make_false() + T(False) >> [](Match&) { return boolfalse(); }, + T(Ellipsis) >> [](Match& _) { return err(_(Ellipsis), "`...` must be after a value in a tuple"); @@ -103,6 +131,7 @@ namespace verona // Compact expressions. In(Expr) * T(Expr) << (Any[Expr] * End) >> [](Match& _) { return _(Expr); }, + T(Expr) << (T(Expr)[Expr] * End) >> [](Match& _) { return _(Expr); }, }; } diff --git a/src/passes/assignlhs.cc b/src/passes/assignlhs.cc index 4d79cb62..29d49fcd 100644 --- a/src/passes/assignlhs.cc +++ b/src/passes/assignlhs.cc @@ -31,21 +31,16 @@ namespace verona return Expr << (TypeAssert << call_lhs(_(Call)) << _(Type)); }, - on_lhs( - T(Expr) << (T(NLRCheck) << (IsImplicit[Implicit] * RhsCall[Call]))) >> - [](Match& _) { - return Expr << (NLRCheck << _(Implicit) << call_lhs(_(Call))); - }, + on_lhs(T(Expr) << (T(NLRCheck) << RhsCall[Call])) >> + [](Match& _) { return Expr << (NLRCheck << call_lhs(_(Call))); }, on_lhs( T(Expr) << (T(TypeAssert) - << ((T(NLRCheck) << (IsImplicit[Implicit] * RhsCall[Call])) * - T(Type)[Type]))) >> + << ((T(NLRCheck) << RhsCall[Call]) * T(Type)[Type]))) >> [](Match& _) { return Expr - << (TypeAssert << (NLRCheck << _(Implicit) << call_lhs(_(Call))) - << _(Type)); + << (TypeAssert << (NLRCheck << call_lhs(_(Call))) << _(Type)); }, // Turn a RefVar on the LHS of an assignment into a RefVarLHS. diff --git a/src/passes/assignment.cc b/src/passes/assignment.cc index 425cda77..6098d6b5 100644 --- a/src/passes/assignment.cc +++ b/src/passes/assignment.cc @@ -14,7 +14,7 @@ namespace verona (T(Expr) << ((T(Let) << T(Ident)[Ident]) / (T(TypeAssert) - << (T(Let) << T(Ident)[Ident]) * T(Type)[Type]))) * + << (T(Expr) << (T(Let) << T(Ident)[Ident])) * T(Type)[Type]))) * T(Expr)[Rhs] * End >> [](Match& _) { return Expr << (Bind << _(Ident) << typevar(_, Type) << _(Rhs)); @@ -43,7 +43,7 @@ namespace verona { auto lhs_e = lhs_child->front(); - if (lhs_e->type() == Let) + if (lhs_e == Let) { // lhs_child is already a Let. lhs_tuple << (Expr << (RefLet << clone(lhs_e / Ident))); @@ -71,7 +71,7 @@ namespace verona // TypeAssert comes after the let bindings for the LHS. if (ty) - seq << (Expr << (TypeAssert << lhs_tuple << ty)); + seq << (Expr << (TypeAssert << (Expr << lhs_tuple) << ty)); // The RHS tuple is the last expression in the sequence. return Expr << (seq << rhs_e << (Expr << rhs_tuple)); diff --git a/src/passes/autocreate.cc b/src/passes/autocreate.cc index cf6e838a..f1289bda 100644 --- a/src/passes/autocreate.cc +++ b/src/passes/autocreate.cc @@ -8,7 +8,7 @@ namespace verona PassDef autocreate() { return { - dir::topdown | dir::once, + dir::bottomup | dir::once, { In(Class) * T(ClassBody)[ClassBody] >> ([](Match& _) -> Node { auto class_body = _(ClassBody); @@ -17,51 +17,42 @@ namespace verona for (auto& node : *class_body) { - if (node->type().in({FieldLet, FieldVar})) - { - auto id = node / Ident; - auto ty = node / Type; - auto def_arg = node / Default; + if (!node->in({FieldLet, FieldVar})) + continue; - // Add each field in order to the call to `new` and the create - // function parameters. - new_args << (Expr << (RefLet << clone(id))); - new_params - << ((Param ^ def_arg) << clone(id) << clone(ty) << def_arg); - } + auto id = node / Ident; + auto ty = node / Type; + auto def_arg = node / Default; + + // Add each field in order to the call to `new` and the create + // function parameters. + new_args << (Expr << (RefLet << clone(id))); + new_params + << ((Param ^ def_arg) + << clone(id) << clone(ty) << clone(def_arg)); } // Create the `new` function, with default arguments set to the field // initializers. Mark `new` as explicit, so that errors when type // checking `new` are reported. - auto body = ClassBody - << *_[ClassBody] + class_body << (Function << Explicit << Rhs << (Ident ^ l_new) << TypeParams << new_params << typevar(_) << DontCare << typepred() - << (Block << (Expr << unit()))); + << (Block << (Expr << Unit))); // If we already have a create function, don't emit one. if (class_body->parent()->lookdown(l_create).empty()) { - // Create the `create` function. - auto fq_new = append_fq(local_fq(_(ClassBody)), selector(l_new)); - - body + class_body << (Function << Implicit << Rhs << (Ident ^ l_create) << TypeParams << clone(new_params) << typevar(_) << DontCare << typepred() - << (Block << (Expr << call(fq_new, new_args)))); + << (Block + << (Expr << New << tuple_to_args(new_args)))); } - return body; + return NoChange; }), - - // Strip the default field values. - T(FieldLet) << (T(Ident)[Ident] * T(Type)[Type] * Any) >> - [](Match& _) { return FieldLet << _(Ident) << _(Type); }, - - T(FieldVar) << (T(Ident)[Ident] * T(Type)[Type] * Any) >> - [](Match& _) { return FieldVar << _(Ident) << _(Type); }, }}; } } diff --git a/src/passes/autofields.cc b/src/passes/autofields.cc index 7dc0456c..6f32669b 100644 --- a/src/passes/autofields.cc +++ b/src/passes/autofields.cc @@ -7,9 +7,9 @@ namespace verona PassDef autofields() { return { - dir::topdown | dir::once, + dir::bottomup | dir::once, { - (T(FieldVar) / T(FieldLet))[Op] << (T(Ident)[Ident] * T(Type)[Type]) >> + T(FieldVar, FieldLet)[Op] << (T(Ident)[Ident] * T(Type)[Type]) >> ([](Match& _) -> Node { // If it's a FieldLet, generate only an RHS function. If it's a // FieldVar, generate an LHS function, which will autogenerate an @@ -17,18 +17,17 @@ namespace verona auto field = _(Op); auto id = _(Ident); auto self_id = _.fresh(l_self); - Token hand = (field->type() == FieldVar) ? Lhs : Rhs; + Token hand = (field == FieldVar) ? Lhs : Rhs; auto expr = FieldRef << (RefLet << (Ident ^ self_id)) << clone(id); if (hand == Rhs) expr = load(expr); - auto f = Function << Implicit << hand << clone(id) << TypeParams - << (Params - << (Param << (Ident ^ self_id) - << (Type << Self) << DontCare)) - << clone(_(Type)) << DontCare << typepred() - << (Block << (Expr << expr)); + auto f = Function + << Implicit << hand << clone(id) << TypeParams + << (Params << (Param << (Ident ^ self_id) << (Type << Self))) + << clone(_(Type)) << DontCare << typepred() + << (Block << (Expr << expr)); return Seq << field << f; }), diff --git a/src/passes/autorhs.cc b/src/passes/autorhs.cc index bbdf00e1..2a5c205b 100644 --- a/src/passes/autorhs.cc +++ b/src/passes/autorhs.cc @@ -8,13 +8,12 @@ namespace verona PassDef autorhs() { return { - dir::topdown | dir::once, + dir::bottomup | dir::once, { T(Function)[Function] << (IsImplicit * T(Lhs) * T(Ident)[Ident] * T(TypeParams)[TypeParams] * T(Params)[Params] * T(Type)[Type] * - T(DontCare) * T(TypePred)[TypePred] * - (T(Block) / T(DontCare))) >> + T(DontCare) * T(TypePred)[TypePred] * T(Block, DontCare)) >> ([](Match& _) -> Node { auto f = _(Function); auto id = _(Ident); @@ -28,8 +27,7 @@ namespace verona for (auto def : defs) { if ( - (def != f) && (def->type() == Function) && - ((def / Ref)->type() != Rhs) && + (def != f) && (def == Function) && ((def / Ref) != Rhs) && ((def / Ident)->location() == id->location()) && ((def / Params)->size() == params->size())) { diff --git a/src/passes/codereuse.cc b/src/passes/codereuse.cc index 0bb33d21..613165dc 100644 --- a/src/passes/codereuse.cc +++ b/src/passes/codereuse.cc @@ -12,44 +12,13 @@ namespace verona Nodes deps; }; - size_t type_substitution(NodeMap& bindings, Node& node) - { - // Substitutes inside of `node`, but not `node` itself. - size_t changes = 0; - - for (auto child : *node) - { - while ((child->type() == FQType) && - ((child / Type)->type() == TypeParamName)) - { - auto l = resolve_fq(child); - auto it = bindings.find(l.def); - - if (it == bindings.end()) - break; - - auto bind = clone(it->second); - - if (bind->type() == Type) - bind = bind / Type; - - node->replace(child, bind); - child = bind; - } - - changes += type_substitution(bindings, child); - } - - return changes; - } - PassDef codereuse() { auto pending = std::make_shared>(); auto ready = std::make_shared(); PassDef codereuse = { - dir::topdown | dir::once, + dir::bottomup | dir::once, { T(Class)[Class] << (T(Ident)[Ident] * T(TypeParams) * @@ -70,29 +39,29 @@ namespace verona if (!l.def) continue; - if (l.def->type() == FQType) + if (l.def == FQType) { auto ll = resolve_fq(l.def); ll.bindings.merge(l.bindings); worklist.push_back(ll); } - else if (l.def->type().in({Type, TypeIsect})) + else if (l.def->in({Type, TypeIsect})) { for (auto& t : *l.def) worklist.emplace_back(l.make(t)); } - else if (l.def->type() == TypeAlias) + else if (l.def == TypeAlias) { // Carry typeargs forward. worklist.emplace_back(l.make(l.def / Type)); } - else if (l.def->type() == Trait) + else if (l.def == Trait) { pend.inherit.push_back(l); } - else if (l.def->type() == Class) + else if (l.def == Class) { - if ((l.def / Inherit / Inherit)->type() == Type) + if ((l.def / Inherit / Inherit) == Type) { // Keep track of the inheritance graph. (*pending)[l.def].deps.push_back(cls); @@ -126,16 +95,16 @@ namespace verona { for (auto f : *(from.def / ClassBody)) { - if (!f->type().in({FieldLet, FieldVar, Function})) + if (!f->in({FieldLet, FieldVar, Function})) continue; // Don't inherit functions without implementations. - if ((f->type() == Function) && ((f / Block)->type() == DontCare)) + if ((f == Function) && ((f / Block) == DontCare)) continue; // If we have an explicit version that conflicts, don't inherit. auto defs = cls->lookdown((f / Ident)->location()); - if (std::any_of(defs.begin(), defs.end(), [&](Node& def) -> bool { + if (std::any_of(defs.begin(), defs.end(), [&](Node& def) { return conflict(f, def); })) continue; @@ -147,7 +116,7 @@ namespace verona changes++; // Type substitution in the inherited member. - changes += type_substitution(from.bindings, f); + changes += from.sub(f); } } diff --git a/src/passes/conditionals.cc b/src/passes/conditionals.cc index 79d8039c..a1cc13b2 100644 --- a/src/passes/conditionals.cc +++ b/src/passes/conditionals.cc @@ -104,22 +104,6 @@ namespace verona _(Else), "`else` must follow an `if` and be followed by an `if` or braces"); }, - - // Strip implicit/explicit marker from fields. - (T(FieldLet) / T(FieldVar))[FieldLet] - << (IsImplicit * T(Ident)[Ident] * T(Type)[Type] * Any[Default]) >> - [](Match& _) { - Node field = _(FieldLet)->type(); - return field << _(Ident) << _(Type) << _(Default); - }, - - // Reset functions marked as implicit to be explicit. - T(Function)[Function] << T(Implicit) >> - [](Match& _) { - auto f = _(Function); - (f / Implicit) = Explicit; - return f; - }, }; } } diff --git a/src/passes/defaultargs.cc b/src/passes/defaultargs.cc index b884e38a..eca4177b 100644 --- a/src/passes/defaultargs.cc +++ b/src/passes/defaultargs.cc @@ -8,81 +8,96 @@ namespace verona PassDef defaultargs() { return { - dir::topdown | dir::once, + dir::bottomup | dir::once, { - T(Function)[Function] - << (T(Explicit) * Hand[Ref] * T(Ident)[Ident] * - T(TypeParams)[TypeParams] * - (T(Params) - << ((T(Param) << (T(Ident) * T(Type) * T(DontCare)))++[Lhs] * - (T(Param) << (T(Ident) * T(Type) * T(NLRCheck)))++[Rhs] * - End)) * - T(Type)[Type] * T(DontCare) * T(TypePred)[TypePred] * - (T(Block) / T(DontCare))[Block]) >> + T(Function) + << (IsImplicit[Implicit] * Hand[Ref] * T(Ident)[Ident] * + T(TypeParams)[TypeParams] * T(Params)[Params] * T(Type)[Type] * + T(LLVMFuncType, DontCare)[LLVMFuncType] * + T(TypePred)[TypePred] * T(Block, DontCare)[Block]) >> [](Match& _) { Node seq = Seq; - auto hand = _(Ref); + auto implicit = _(Implicit)->type(); + auto hand = _(Ref)->type(); auto id = _(Ident); auto tp = _(TypeParams); + auto params = _(Params); auto ty = _(Type); + auto llvmty = _(LLVMFuncType); auto pred = _(TypePred); - auto lhs = _[Lhs]; - auto rhs = _[Rhs]; - auto fq = local_fq(_(Function)); - Node params = Params; + Node new_params = Params; Node args = Tuple; + bool has_default = false; - // Start with parameters that have no default value. - for (auto it = lhs.first; it != lhs.second; ++it) + for (auto& param : *params) { - auto param_id = *it / Ident; - params << (Param << clone(param_id) << clone(*it / Type)); + auto param_id = param / Ident; + auto param_type = param / Type; + auto block = param / Default; args << (Expr << (RefLet << clone(param_id))); - } - for (auto it = rhs.first; it != rhs.second; ++it) - { - // At this point, the default argument is a create call on the - // anonymous class derived from the lambda. Apply the created - // lambda to get the default argument. - auto def_arg = call(selector(l_apply), (*it / Default)); + if (block == DontCare) + { + if (has_default) + { + params->replace( + param, + err( + param, + "Can't put a parameter with no default value after a " + "parameter with one")); + } + } + else + { + has_default = true; + auto def_arg = block->back(); - // Add the default argument to the forwarding call. - args << (Expr << def_arg); + // Syntactically, the last statement in the block is the default + // argument expression. WF doesn't enforce this. + if (def_arg == Expr) + block->pop_back(); + else + def_arg = Expr << Unit; - // Add a new function that calls the arity+1 function. Mark it as - // explicit, so that errors when type checking the default - // arguments are reported. - seq - << (Function - << Explicit << clone(hand) << clone(id) << clone(tp) - << clone(params) << clone(ty) << DontCare << clone(pred) - << (Block << (Expr << (call(clone(fq), clone(args)))))); + // Evaluate the default argument and call the arity+1 function. + block << (Expr + << (Assign << (Expr << (Let << (Ident ^ param_id))) + << def_arg)) + << (Expr << Self << DoubleColon << selector(id) + << tuple_to_args(clone(args))); - // Add a parameter. - auto param_id = *it / Ident; - params << (Param << clone(param_id) << clone(*it / Type)); + // Add a new function that calls the arity+1 function. Mark it + // as explicit, so that errors when type checking the default + // arguments are reported. + seq + << (Function << implicit << hand << clone(id) << clone(tp) + << clone(new_params) << clone(ty) + << clone(llvmty) << clone(pred) << block); + } - // Replace the last argument with a reference to the parameter. - args->pop_back(); - args << (Expr << (RefLet << clone(param_id))); + // Add the parameter to the new parameter list. + new_params << (Param << clone(param_id) << clone(param_type)); } // The original function, with no default arguments. return seq - << (Function << Explicit << hand << id << tp << params << ty - << DontCare << pred << _(Block)); + << (Function << implicit << hand << id << tp << new_params << ty + << llvmty << pred << _(Block)); }, - T(Param) << (T(Ident)[Ident] * T(Type)[Type] * T(DontCare)) >> - [](Match& _) { return Param << _(Ident) << _(Type); }, - - T(Param)[Param] << (T(Ident) * T(Type) * T(NLRCheck)) >> + // Strip the default field values. + T(FieldLet) + << (IsImplicit[Implicit] * T(Ident)[Ident] * T(Type)[Type] * Any) >> [](Match& _) { - return err( - _(Param), - "Can't put a default value before a non-defaulted value"); + return FieldLet << _(Implicit) << _(Ident) << _(Type); + }, + + T(FieldVar) + << (IsImplicit[Implicit] * T(Ident)[Ident] * T(Type)[Type] * Any) >> + [](Match& _) { + return FieldVar << _(Implicit) << _(Ident) << _(Type); }, }}; } diff --git a/src/passes/defbeforeuse.cc b/src/passes/defbeforeuse.cc index 51c24b51..74181457 100644 --- a/src/passes/defbeforeuse.cc +++ b/src/passes/defbeforeuse.cc @@ -8,7 +8,7 @@ namespace verona PassDef defbeforeuse() { return { - dir::topdown | dir::once, + dir::bottomup | dir::once, { T(RefLet) << T(Ident)[Ident] >> ([](Match& _) -> Node { if (!is_implicit(_(Ident)) && !lookup_type(_(Ident), {Bind, Param})) diff --git a/src/passes/drop.cc b/src/passes/drop.cc index d5750658..3cc56e8f 100644 --- a/src/passes/drop.cc +++ b/src/passes/drop.cc @@ -51,13 +51,13 @@ namespace verona return true; // Only check parents and successors if this isn't an early return. - return (block->back()->type() != Return) && + return (block->back() != Return) && (is_parent(of, block) || is_successor_or_child(of, block)); } bool is_parent(Node of, Node block) { - if (of->parent()->type() == Function) + if (of->parent() == Function) return false; auto& parent = parents.at(of); @@ -129,7 +129,7 @@ namespace verona { auto ref = it->second; auto parent = ref->parent(); - bool immediate = parent->type() == Block; + bool immediate = parent == Block; if (immediate && (parent->back() != ref)) parent->replace(ref); @@ -151,7 +151,7 @@ namespace verona auto ref = it->second; auto id = ref / Ident; auto parent = ref->parent()->shared_from_this(); - bool immediate = parent->type() == Block; + bool immediate = parent == Block; bool discharging = true; // We're the last use if there is no following use in this or any @@ -223,16 +223,10 @@ namespace verona auto drop_map = std::make_shared>(); PassDef drop = { - dir::topdown | dir::once, + dir::bottomup | dir::once, { - (T(Param) / T(Bind)) << T(Ident)[Ident] >> - ([drop_map](Match& _) -> Node { - drop_map->back().gen(_(Ident)->location()); - return NoChange; - }), - - T(RefLet)[RefLet] << T(Ident)[Ident] >> ([drop_map](Match& _) -> Node { - drop_map->back().ref(_(Ident)->location(), _(RefLet)); + T(Param, Bind) << T(Ident)[Ident] >> ([drop_map](Match& _) -> Node { + drop_map->back().gen(_(Ident)->location()); return NoChange; }), @@ -250,19 +244,26 @@ namespace verona }), }}; + drop.pre(Function, [drop_map](Node f) { + auto llvm = (f / LLVMFuncType) == LLVMFuncType; + drop_map->push_back(track(llvm)); + return 0; + }); + drop.pre(Block, [drop_map](Node node) { drop_map->back().pre_block(node); return 0; }); - drop.post(Block, [drop_map](Node) { - drop_map->back().post_block(); + drop.post(RefLet, [drop_map](Node node) { + // RefLet is handled with a post-order step to avoid an immediate RefLet + // that precedes a nested RefLet being handled after the nested RefLet. + drop_map->back().ref((node / Ident)->location(), node); return 0; }); - drop.pre(Function, [drop_map](Node f) { - auto llvm = (f / LLVMFuncType)->type() == LLVMFuncType; - drop_map->push_back(track(llvm)); + drop.post(Block, [drop_map](Node) { + drop_map->back().post_block(); return 0; }); diff --git a/src/passes/lambda.cc b/src/passes/lambda.cc index 84c2130a..a423371f 100644 --- a/src/passes/lambda.cc +++ b/src/passes/lambda.cc @@ -10,118 +10,100 @@ namespace verona auto freevars = std::make_shared>>(); PassDef lambda = - {dir::bottomup, - { - T(RefLet) << T(Ident)[Ident] >> ([freevars](Match& _) -> Node { - if (!freevars->empty()) - { - // If we don't have a definition within the scope of the lambda, - // then it's a free variable. - auto id = _(Ident); + { + dir::bottomup | dir::once, + { + T(RefLet) << T(Ident)[Ident] >> ([freevars](Match& _) -> Node { + if (!freevars->empty()) + { + // If we don't have a definition within the scope of the lambda, + // then it's a free variable. + auto id = _(Ident); - if (id->lookup(id->parent(Lambda)).empty()) - freevars->back().insert(id->location()); - } + if (id->lookup(id->parent(Lambda)).empty()) + freevars->back().insert(id->location()); + } - return NoChange; - }), + return NoChange; + }), - T(NLRCheck) << (T(Explicit) * T(Call)[Call]) >> ([](Match& _) -> Node { - // Mark NLRCheck inside a Lambda as Implicit, to prevent future - // unwrapping in the nlrcheck pass. - auto call = _(Call); + T(Lambda)[Lambda] + << (T(TypeParams)[TypeParams] * T(Params)[Params] * + T(Type)[Type] * T(TypePred)[TypePred] * T(Block)[Block]) >> + [freevars](Match& _) { + // Create the anonymous type. + Node class_body = ClassBody; + auto class_id = _.fresh(l_lambda); + auto classdef = Class << (Ident ^ class_id) << TypeParams + << (Inherit << DontCare) << typepred() + << class_body; - if ( - call->parent({Lambda, FieldLet, FieldVar, Param, Function}) - ->type() == Lambda) - return NLRCheck << Implicit << call; + // The create function will capture the free variables. + Node create_params = Params; + Node create_args = Tuple; + Node new_args = Tuple; - return NoChange; - }), + Node apply_body = Block; + auto self_id = _.fresh(l_self); + auto& fv = freevars->back(); - T(Lambda)[Lambda] - << (T(TypeParams)[TypeParams] * T(Params)[Params] * T(Type)[Type] * - T(TypePred)[TypePred] * T(Block)[Block]) >> - [freevars](Match& _) { - // Create the anonymous type. - Node class_body = ClassBody; - auto class_id = _.fresh(l_class); - auto classdef = Class << (Ident ^ class_id) << TypeParams - << (Inherit << DontCare) << typepred() - << class_body; + std::for_each( + fv.begin(), fv.end(), [&](auto& fv_id) { + // Add a field for the free variable to the anonymous type. + auto type_id = _.fresh(l_typevar); + class_body + << (FieldLet << Explicit << (Ident ^ fv_id) + << typevar(type_id) << DontCare); - // Build an FQType for the anonymous type. - auto fq = append_fq( - local_fq(_(Lambda)->parent({Class, Trait})), - TypeClassName << (Ident ^ class_id) << TypeArgs); + // Add a parameter to the create function to capture the free + // variable as a field. + create_params + << (Param << (Ident ^ fv_id) << typevar(type_id) + << DontCare); + new_args << (Expr << (RefLet << (Ident ^ fv_id))); - // The create function will capture the free variables. - auto fq_new = append_fq(fq, selector(l_new)); - Node new_args = Tuple; + // Add an argument to the create call. Don't load the free + // variable, even if it was a `var`. + create_args << (Expr << (RefLet << (Ident ^ fv_id))); - auto fq_create = append_fq(fq, selector(l_create)); - Node create_params = Params; - Node create_args = Tuple; + // At the start of the lambda body, assign the field to a + // local variable with the same name as the free variable. + apply_body + << (Expr + << (Assign << (Expr + << (TypeAssert + << (Expr << (Let << (Ident ^ fv_id))) + << typevar(type_id))) + << (Expr << (RefLet << (Ident ^ self_id)) + << Dot << selector(fv_id)))); + }); - Node apply_body = Block; - auto self_id = _.fresh(l_self); - auto& fv = freevars->back(); + // The apply function is the original lambda. Prepend a + // `self`-like parameter with a fresh name to the lambda + // parameters. + // TODO: capability for Self + auto apply_func = Function + << LambdaFunc << Rhs << (Ident ^ l_apply) << _(TypeParams) + << (Params << (Param << (Ident ^ self_id) << (Type << Self) + << DontCare) + << *_[Params]) + << _(Type) << DontCare << _(TypePred) + << (apply_body << *_[Block]); - std::for_each( - fv.begin(), fv.end(), [&](auto& fv_id) { - // Add a field for the free variable to the anonymous type. - auto type_id = _.fresh(l_typevar); - class_body - << (FieldLet << (Ident ^ fv_id) - << (Type << (TypeVar ^ type_id)) << DontCare); + // Add the create and apply functions to the anonymous type. + auto create_func = Function + << Implicit << Rhs << (Ident ^ l_create) << TypeParams + << create_params << typevar(_) << DontCare << typepred() + << (Block << (Expr << New << tuple_to_args(new_args))); - // Add a parameter to the create function to capture the free - // variable as a field. - create_params - << (Param << (Ident ^ fv_id) << (Type << (TypeVar ^ type_id)) - << DontCare); - new_args << (Expr << (RefLet << (Ident ^ fv_id))); + class_body << create_func << apply_func; + freevars->pop_back(); - // Add an argument to the create call. Don't load the free - // variable, even if it was a `var`. - create_args << (Expr << (RefLet << (Ident ^ fv_id))); - - // At the start of the lambda body, assign the field to a - // local variable with the same name as the free variable. - apply_body - << (Expr - << (Bind << (Ident ^ fv_id) - << (Type << (TypeVar ^ type_id)) - << (Expr << call( - selector(fv_id), - RefLet << (Ident ^ self_id))))); - }); - - // The apply function is the original lambda. Prepend a - // `self`-like parameter with a fresh name to the lambda - // parameters. - // TODO: capability for Self - auto apply_func = Function - << Implicit << Rhs << (Ident ^ l_apply) << _(TypeParams) - << (Params << (Param << (Ident ^ self_id) << (Type << Self) - << DontCare) - << *_[Params]) - << _(Type) << DontCare << _(TypePred) - << (apply_body << *_[Block]); - - // Add the create and apply functions to the anonymous type. - auto create_func = Function - << Implicit << Rhs << (Ident ^ l_create) << TypeParams - << create_params << typevar(_) << DontCare << typepred() - << (Block << (Expr << call(fq_new, new_args))); - - class_body << create_func << apply_func; - freevars->pop_back(); - - return Seq << (Lift << ClassBody << classdef) - << call(fq_create, create_args); - }, - }}; + return Seq << (Lift << Block << classdef) + << (Expr << (Ident ^ class_id) + << tuple_to_args(create_args)); + }, + }}; lambda.pre(Lambda, [freevars](Node) { freevars->push_back({}); diff --git a/src/passes/memberconflict.cc b/src/passes/memberconflict.cc index a4bd17b9..59181716 100644 --- a/src/passes/memberconflict.cc +++ b/src/passes/memberconflict.cc @@ -7,11 +7,11 @@ namespace verona PassDef memberconflict() { return { - dir::topdown | dir::once, + dir::bottomup | dir::once, { - (T(FieldLet) / T(FieldVar) / T(Function))[Op] >> ([](Match& _) -> Node { + T(FieldLet, FieldVar, Function)[Op] >> ([](Match& _) -> Node { auto f = _(Op); - bool implicit = (f / Implicit)->type() == Implicit; + bool implicit = (f / Implicit) == Implicit; auto defs = f->scope()->lookdown((f / Ident)->location()); Nodes conflicts; @@ -20,7 +20,7 @@ namespace verona if (!conflict(f, def)) continue; - if (implicit == ((def / Implicit)->type() == Implicit)) + if (implicit == ((def / Implicit) == Implicit)) { // If both are implicit or both are explicit, it's an error. if (def->precedes(f)) @@ -44,7 +44,7 @@ namespace verona Node e = Error; auto p = f->parent({Class, Trait}); - if (p->type() == Class) + if (p == Class) e << (ErrorMsg ^ "Member conflict in this class") << (ErrorAst ^ (p / Ident)); else diff --git a/src/passes/modules.cc b/src/passes/modules.cc index b23b5a83..d2ce0766 100644 --- a/src/passes/modules.cc +++ b/src/passes/modules.cc @@ -35,22 +35,22 @@ namespace verona // Otherwise end the type at a Where, Brace, or TripleColon. T(Colon) * (~T(Brace) * - (((T(Symbol) / T(Dot)) * T(Brace)) / - (!(T(Where) / T(Brace) / T(TripleColon))))++)[Type] >> + ((T(Symbol, Dot) * T(Brace)) / + (!T(Where, Brace, TripleColon)))++)[Type] >> [](Match& _) { return Type << (_[Type] || DontCare); }, // Type predicate. T(Where) * (~T(Brace) * - (((T(Symbol) / T(Dot)) * T(Brace)) / - (!(T(Where) / T(Brace) / T(TripleColon))))++)[Type] >> + ((T(Symbol, Dot) * T(Brace)) / + (!T(Where, Brace, TripleColon)))++)[Type] >> [](Match& _) { return TypePred << (Type << (_[Type] || TypeTrue)); }, T(TripleColon) * (T(Paren) - << ((T(List) << (T(Group) << (T(Ident) / T(LLVM)))++[Args]) / - ~(T(Group) << (T(Ident) / T(LLVM)))[Args])) * - T(Symbol, "->") * (T(Ident) / T(LLVM))[Return] >> + << ((T(List) << (T(Group) << T(Ident, LLVM))++[Args]) / + ~(T(Group) << T(Ident, LLVM))[Args])) * + T(Symbol, "->") * T(Ident, LLVM)[Return] >> [](Match& _) { return LLVMFuncType << (LLVMList << *_[Args]) << _(Return); }, diff --git a/src/passes/nlrcheck.cc b/src/passes/nlrcheck.cc index ae4bc4eb..2fb8f302 100644 --- a/src/passes/nlrcheck.cc +++ b/src/passes/nlrcheck.cc @@ -7,12 +7,10 @@ namespace verona PassDef nlrcheck() { return { - dir::topdown | dir::once, + dir::bottomup | dir::once, { T(NLRCheck) - << (IsImplicit[Implicit] * - (T(Call)[Call] - << ((T(Selector) / T(FQFunction))[Op] * T(Args)))) >> + << (T(Call)[Call] << (T(Selector, FQFunction)[Op] * T(Args))) >> [](Match& _) { auto call = _(Call); @@ -27,10 +25,10 @@ namespace verona auto nlr = Type << nonlocal(_); Node ret = Cast << ref << nlr; - // Unwrap if we're in a function (Explicit), but not if we're in a - // lambda (Implicit). - if (_(Implicit)->type() == Explicit) - ret = load(ret, true); + // Unwrap if we're in a function, but not if we're in a lambda. + // Remove the NLRCheck around the load call. + if (call->parent(Function) / Implicit != LambdaFunc) + ret = load(ret) / Call; return ExprSeq << (Expr << (Bind << (Ident ^ id) << typevar(_) diff --git a/src/passes/partialapp.cc b/src/passes/partialapp.cc index b1615b7a..8b8fdbb2 100644 --- a/src/passes/partialapp.cc +++ b/src/passes/partialapp.cc @@ -11,7 +11,7 @@ namespace verona // This function extracts all typeparams from a type `t` that are defined // within `scope` and appends them to `tp` if they aren't already present. - if (t->type().in( + if (t->in( {Type, TypeArgs, TypeUnion, @@ -23,17 +23,17 @@ namespace verona for (auto& tt : *t) extract_typeparams(scope, tt, tp); } - else if (t->type().in({TypeClassName, TypeAliasName, TypeTraitName})) + else if (t->in({TypeClassName, TypeAliasName, TypeTraitName})) { extract_typeparams(scope, t / Lhs, tp); extract_typeparams(scope, t / TypeArgs, tp); } - else if (t->type() == TypeParamName) + else if (t == TypeParamName) { auto id = t / Ident; auto defs = id->lookup(scope); - if ((defs.size() == 1) && ((*defs.begin())->type() == TypeParam)) + if ((defs.size() == 1) && ((*defs.begin()) == TypeParam)) { if (!std::any_of(tp->begin(), tp->end(), [&](auto& p) { return (p / Ident)->location() == id->location(); @@ -52,7 +52,7 @@ namespace verona { // This finds all typeparams in a Class or Function definition and builds // a TypeArgs that contains all of them, in order. - if (!node->type().in({Class, Function})) + if (!node->in({Class, Function})) return typeargs; for (auto typeparam : *(node / TypeParams)) @@ -82,15 +82,13 @@ namespace verona T(Function)[Function] << (IsImplicit * Hand[Ref] * T(Ident)[Ident] * T(TypeParams)[TypeParams] * T(Params)[Params] * T(Type) * - T(DontCare) * T(TypePred)[TypePred] * - (T(Block) / T(DontCare))) >> - [](Match& _) { + T(DontCare) * T(TypePred)[TypePred] * T(Block, DontCare)) >> + ([](Match& _) -> Node { auto f = _(Function); + auto parent = f->parent({Class, Trait}); + auto hand = _(Ref)->type(); auto id = _(Ident); auto params = _(Params); - auto parent = f->parent({Class, Trait}); - auto fq_f = local_fq(f); - auto fq_parent = local_fq(parent); // Find the lowest arity that is not already defined. If an arity 5 // and an arity 3 function `f` are provided, an arity 4 partial @@ -103,7 +101,7 @@ namespace verona for (auto def : defs) { - if ((def == f) || (def->type() != Function)) + if ((def == f) || (def != Function)) continue; auto arity = (def / Params)->size(); @@ -112,14 +110,61 @@ namespace verona start_arity = std::max(start_arity, arity + 1); } + if (start_arity == end_arity) + return NoChange; + + // We will be returning the original function, plus some number of + // partial application functions and their anonymous classes. Make + // the local FQ before putting `f` into the Seq node. + auto fq_f = local_fq(f); + Node ret = Seq << f; + + // If the parent is a trait, generate the partial application + // function prototypes, but no implementations. + if (parent == Trait) + { + for (auto arity = start_arity; arity < end_arity; ++arity) + { + Node func_tp = TypeParams; + Node func_params = Params; + + for (size_t i = 0; i < arity; ++i) + { + auto param = params->at(i); + auto param_id = param / Ident; + auto param_type = param / Type; + + // Add any needed typeparams. + extract_typeparams(f, param_type, func_tp); + + // Add the parameter to the partial function. + func_params << clone(param); + } + + ret + << (Function << Implicit << hand << clone(id) << func_tp + << func_params << typevar(_) << DontCare + << typepred() << DontCare); + } + + return ret; + } + // Create a unique anonymous class name for each arity. Nodes names; + auto basename = std::string("partial.") + .append(hand.str()) + .append(".") + .append(id->location().view()) + .append("/"); for (auto arity = start_arity; arity < end_arity; ++arity) - names.push_back(Ident ^ _.fresh(l_class)); + { + names.push_back( + Ident ^ _.fresh(basename + std::to_string(arity))); + } - Node ret = Seq << f; - auto hand = _(Ref)->type(); + auto fq_parent = local_fq(parent); Nodes fqs; Nodes classbodies; @@ -141,6 +186,7 @@ namespace verona // The anonymous class has fields for each supplied argument and a // create function that captures the supplied arguments. auto fq_new = append_fq(fq_class, selector(l_new)); + Node new_params = Params; Node new_args = Tuple; // Find all needed typeparams and add them. @@ -169,6 +215,7 @@ namespace verona // Add the argument to the `new` call inside the class create // function. new_args << (Expr << (RefLet << clone(param_id))); + new_params << (Param << clone(param_id) << clone(param_type)); // Add the parameter to the partial function. func_params << clone(param); @@ -184,6 +231,12 @@ namespace verona << (Block << (Expr << call(fq_new, new_args))); classbody << create_func; + // Create the `new` function. + classbody + << (Function << Explicit << Rhs << (Ident ^ l_new) << TypeParams + << new_params << typevar(_) << DontCare + << typepred() << (Block << (Expr << unit()))); + // Create the partial function that returns the anonymous class. auto fq_create = append_fq( fq_class, @@ -266,7 +319,7 @@ namespace verona } return ret; - }, + }), }}; } } diff --git a/src/passes/reference.cc b/src/passes/reference.cc index dc8fc964..267777b9 100644 --- a/src/passes/reference.cc +++ b/src/passes/reference.cc @@ -11,10 +11,9 @@ namespace verona // LLVM literal. In(Expr) * T(LLVM)[LLVM] * T(Ident)[Lhs] * T(Ident)++[Rhs] >> [](Match& _) { - auto llvm = _(LLVM); auto rhs = _[Rhs]; auto s = std::string() - .append(llvm->location().view()) + .append(_(LLVM)->location().view()) .append(" %") .append(_(Lhs)->location().view()); @@ -33,134 +32,30 @@ namespace verona .append(_(Rhs)->location().view()); }, - // Dot notation. Use `Ident` as a selector, even if it's in scope. - In(Expr) * T(Dot) * (T(Ident) / T(Symbol))[Ident] * + // Dot and DoubleColon notation. Use `Ident` as a selector, even if it's + // in scope. + In(Expr) * T(Dot, DoubleColon)[Dot] * T(Ident, Symbol)[Ident] * ~T(TypeArgs)[TypeArgs] >> - [](Match& _) { return Seq << Dot << selector(_(Ident), _(TypeArgs)); }, + [](Match& _) { + return Seq << _(Dot) << selector(_(Ident), _(TypeArgs)); + }, // Local reference. - In(Expr) * - T(Ident)[Ident]([](auto& n) { return lookup_type(n, {Var}); }) >> - [](Match& _) { return RefVar << _(Ident); }, + In(Expr) * T(Ident)[Ident] >> ([](Match& _) -> Node { + auto id = _(Ident); - In(Expr) * T(Ident)[Ident]([](auto& n) { - return lookup_type(n, {Let, Param}); - }) >> - [](Match& _) { return RefLet << _(Ident); }, + if (lookup_type(id, {Var})) + return RefVar << id; + else if (lookup_type(id, {Let, Param})) + return RefLet << id; - // Unscoped reference. - In(Expr) * (T(Ident) / T(Symbol))[Ident] * ~T(TypeArgs)[TypeArgs] >> + return NoChange; + }), + + // Given `try { ... }`, apply the lambda. + In(Expr) * T(Try)[Try] * T(Lambda)[Lambda] * --T(Dot) >> [](Match& _) { - auto id = _(Ident); - auto ta = _(TypeArgs); - auto defs = lookup(id, ta); - - if (defs.size() == 1) - { - auto def = *defs.begin(); - - if (def.too_many_typeargs) - { - return Error << (ErrorMsg ^ "too many type arguments") - << ((ErrorAst ^ id) << id << ta); - } - - auto fq = make_fq(def); - - if (fq->type() == FQType) - return fq; - } - - if (defs.size() > 1) - { - // If there are multiple definitions, it's an ambiguous reference. - auto err = Error << (ErrorMsg ^ "ambiguous reference") - << ((ErrorAst ^ id) << id << ta); - - for (auto& other : defs) - err << (ErrorAst ^ (other.def / Ident)); - - return err; - } - - // If there isn't a single type definition, treat it as a selector. - return selector(id, ta); - }, - - // Scoped reference. - In(Expr) * - (T(FQType)[Lhs] * T(DoubleColon) * (T(Ident) / T(Symbol))[Ident] * - ~T(TypeArgs)[TypeArgs]) >> - [](Match& _) { - auto id = _(Ident); - auto ta = _(TypeArgs); - auto def = resolve_fq(_(Lhs)); - auto defs = lookdown(def, id, ta); - - if (defs.size() == 0) - return Error << (ErrorMsg ^ "unknown reference") - << ((ErrorAst ^ id) << id << ta); - - if (defs.size() == 1) - { - auto ldef = *defs.begin(); - - if (ldef.too_many_typeargs) - { - return Error << (ErrorMsg ^ "too many type arguments") - << ((ErrorAst ^ id) << id << ta); - } - - return make_fq(ldef); - } - - if (std::any_of(defs.begin(), defs.end(), [](auto& d) { - return d.def->type() != Function; - })) - { - // If there are multiple definitions, and at least one of them is - // not a function, then we have an ambiguous reference. - auto err = Error << (ErrorMsg ^ "ambiguous reference") - << ((ErrorAst ^ id) << id << ta); - - for (auto& other : defs) - err << (ErrorAst ^ (other.def / Ident)); - - return err; - } - - // Select the smallest arity function. - auto l = - *std::min_element(defs.begin(), defs.end(), [](auto& a, auto& b) { - return (a.def / Params)->size() < (b.def / Params)->size(); - }); - - return make_fq(l); - }, - - // Error out on invalid scoped references. - In(Expr) * - (T(DoubleColon) * ~(T(Ident) / T(Symbol)) * - ~T(TypeArgs))[DoubleColon] >> - [](Match& _) { - return err(_(DoubleColon), "Expected a scoped reference"); - }, - - // Create sugar, with no arguments. - In(Expr) * T(FQType)[FQType] * ~T(TypeArgs)[TypeArgs] >> - [](Match& _) { - return append_fq(_(FQType), selector(l_create, _(TypeArgs))); - }, - - // Lone TypeArgs are typeargs on apply. - In(Expr) * T(TypeArgs)[TypeArgs] >> - [](Match& _) { return Seq << Dot << selector(l_apply, _(TypeArgs)); }, - - // New sugar. - In(Expr) * T(New)[New] >> - [](Match& _) { - return append_fq( - local_fq(_(New)->parent({Class, Trait})), selector(l_new)); + return Seq << Try << _(Lambda) << Dot << selector(l_apply); }, }; } diff --git a/src/passes/resetimplicit.cc b/src/passes/resetimplicit.cc new file mode 100644 index 00000000..e0a2c6d7 --- /dev/null +++ b/src/passes/resetimplicit.cc @@ -0,0 +1,28 @@ +// Copyright Microsoft and Project Verona Contributors. +// SPDX-License-Identifier: MIT +#include "../lang.h" + +namespace verona +{ + PassDef resetimplicit() + { + return { + dir::bottomup | dir::once, + { + // Reset everything marked as implicit to be explicit. + T(Implicit) >> ([](Match&) -> Node { return Explicit; }), + + // Strip implicit/explicit marker from fields. + T(FieldLet) << (IsImplicit * T(Ident)[Ident] * T(Type)[Type]) >> + [](Match& _) { return FieldLet << _(Ident) << _(Type); }, + + T(FieldVar) << (IsImplicit * T(Ident)[Ident] * T(Type)[Type]) >> + [](Match& _) { return FieldVar << _(Ident) << _(Type); }, + + // Remove all `use` statements. + In(ClassBody) * T(Use) >> ([](Match&) -> Node { return {}; }), + + In(Block) * T(Use) >> ([](Match&) -> Node { return Expr << Unit; }), + }}; + } +} diff --git a/src/passes/reverseapp.cc b/src/passes/reverseapp.cc index 16d6aba8..903acd17 100644 --- a/src/passes/reverseapp.cc +++ b/src/passes/reverseapp.cc @@ -7,10 +7,6 @@ namespace verona PassDef reverseapp() { return { - // Remove all `use` statements. - In(ClassBody) * T(Use) >> ([](Match&) -> Node { return {}; }), - In(Block) * T(Use) >> ([](Match&) -> Node { return Expr << Unit; }), - // Dot: reverse application. This binds most strongly. (Object / Operator)[Lhs] * T(Dot) * Operator[Rhs] >> [](Match& _) { return call(_(Rhs), _(Lhs)); }, diff --git a/src/passes/structure.cc b/src/passes/structure.cc index e50bd4d7..b663e6a0 100644 --- a/src/passes/structure.cc +++ b/src/passes/structure.cc @@ -8,7 +8,7 @@ namespace verona { if (!name) name = Ident ^ l_apply; - else if (name->type() == Symbol) + else if (name == Symbol) name = Ident ^ name; return name; @@ -22,25 +22,22 @@ namespace verona In(ClassBody) * (T(Equals) << ((T(Group) - << ((T(Let) / T(Var))[Let] * T(Ident)[Ident] * ~T(Type)[Type] * + << (T(Let, Var)[Let] * T(Ident)[Ident] * ~T(Type)[Type] * End)) * T(Group)++[Rhs])) >> [](Match& _) { - Node node = _(Let)->type() == Let ? FieldLet : FieldVar; + Node node = _(Let) == Let ? FieldLet : FieldVar; return node << Explicit << _(Ident) << typevar(_, Type) - << (Lambda << TypeParams << Params << typevar(_) - << typepred() - << (Block << (Expr << (Default << _[Rhs])))); + << (Block << (Expr << (Default << _[Rhs]))); }, // Field without a default value. // (group let|var ident type) In(ClassBody) * (T(Group) - << ((T(Let) / T(Var))[Let] * T(Ident)[Ident] * ~T(Type)[Type] * - End)) >> + << (T(Let, Var)[Let] * T(Ident)[Ident] * ~T(Type)[Type] * End)) >> [](Match& _) { - Node node = _(Let)->type() == Let ? FieldLet : FieldVar; + Node node = _(Let) == Let ? FieldLet : FieldVar; return node << Explicit << _(Ident) << typevar(_, Type) << DontCare; }, @@ -50,7 +47,7 @@ namespace verona In(ClassBody) * (T(Equals) << ((T(Group) - << (~T(Ref)[Ref] * ~(T(Ident) / T(Symbol))[Ident] * + << (~T(Ref)[Ref] * ~T(Ident, Symbol)[Ident] * ~T(Square)[TypeParams] * T(Paren)[Params] * ~T(Type)[Type] * ~T(LLVMFuncType)[LLVMFuncType] * ~T(TypePred)[TypePred] * T(Brace)[Block] * (Any * Any++)[Lhs])) * @@ -71,7 +68,7 @@ namespace verona In(ClassBody) * (T(Equals) << ((T(Group) - << (~T(Ref)[Ref] * ~(T(Ident) / T(Symbol))[Ident] * + << (~T(Ref)[Ref] * ~T(Ident, Symbol)[Ident] * ~T(Square)[TypeParams] * T(Paren)[Params] * ~T(Type)[Type] * ~T(LLVMFuncType)[LLVMFuncType] * ~T(TypePred)[TypePred] * End)) * @@ -89,8 +86,8 @@ namespace verona // Function: f[T](x: T = e): T { e } // (group name typeparams params type llvmtype typepred brace) In(ClassBody) * T(Group) - << (~T(Ref)[Ref] * ~(T(Ident) / T(Symbol))[Ident] * - ~T(Square)[TypeParams] * T(Paren)[Params] * ~T(Type)[Type] * + << (~T(Ref)[Ref] * ~T(Ident, Symbol)[Ident] * ~T(Square)[TypeParams] * + T(Paren)[Params] * ~T(Type)[Type] * ~T(LLVMFuncType)[LLVMFuncType] * ~T(TypePred)[TypePred] * ~T(Brace)[Block] * (Any++)[Rhs]) >> [](Match& _) { @@ -129,10 +126,10 @@ namespace verona T(Group)++[Rhs]) >> [](Match& _) { return ValueParam << _(Ident) << _(Type) - << (Expr << (Default << _[Rhs])); + << (Block << (Expr << (Default << _[Rhs]))); }, - In(TypeParams) * (!(T(TypeParam) / T(ValueParam)))[TypeParam] >> + In(TypeParams) * (!T(TypeParam, ValueParam))[TypeParam] >> [](Match& _) { return err( _(TypeParam), "Expected a type parameter or a value parameter"); @@ -149,34 +146,29 @@ namespace verona // Param: (group ident type) In(Params) * T(Group) - << ((T(Ident) / T(DontCare))[Ident] * ~T(Type)[Type] * End) >> + << (T(Ident, DontCare)[Ident] * ~T(Type)[Type] * End) >> [](Match& _) { - auto id = (_(Ident)->type() == DontCare) ? - (Ident ^ _.fresh(l_param)) : - _(Ident); + auto id = + (_(Ident) == DontCare) ? (Ident ^ _.fresh(l_param)) : _(Ident); return Param << id << typevar(_, Type) << DontCare; }, // Param: (equals (group ident type) group) In(Params) * T(Equals) - << ((T(Group) - << ((T(Ident) / T(DontCare))[Ident] * ~T(Type)[Type] * End)) * + << ((T(Group) << (T(Ident, DontCare)[Ident] * ~T(Type)[Type] * End)) * T(Group)++[Expr]) >> [](Match& _) { - auto id = (_(Ident)->type() == DontCare) ? - (Ident ^ _.fresh(l_param)) : - _(Ident); + auto id = + (_(Ident) == DontCare) ? (Ident ^ _.fresh(l_param)) : _(Ident); return Param << id << typevar(_, Type) - << (Lambda << TypeParams << Params << typevar(_) - << typepred() - << (Block << (Expr << (Default << _[Expr])))); + << (Block << (Expr << (Default << _[Expr]))); }, In(Params) * (!T(Param))[Param] >> [](Match& _) { return err(_(Param), "Expected a parameter"); }, // Use. - (In(ClassBody) / In(Block)) * T(Group) << T(Use)[Use] * (Any++)[Type] >> + In(ClassBody, Block) * T(Group) << T(Use)[Use] * (Any++)[Type] >> [](Match& _) { return (Use ^ _(Use)) << (Type << (_[Type] || DontCare)); }, @@ -185,7 +177,7 @@ namespace verona [](Match& _) { return err(_(Use), "Can't put a `use` here"); }, // TypeAlias: (equals (group typealias typeparams typepred) group) - (In(ClassBody) / In(Block)) * T(Equals) + In(ClassBody, Block) * T(Equals) << ((T(Group) << (T(TypeAlias) * T(Ident)[Ident] * ~T(Square)[TypeParams] * ~T(TypePred)[TypePred] * End)) * @@ -196,7 +188,7 @@ namespace verona << (Type << (Default << _[Rhs])); }, - (In(ClassBody) / In(Block)) * T(TypeAlias)[TypeAlias] << End >> + In(ClassBody, Block) * T(TypeAlias)[TypeAlias] << End >> [](Match& _) { return err(_(TypeAlias), "Expected a `type` definition"); }, @@ -207,7 +199,7 @@ namespace verona // Class. // (group class ident typeparams type typepred brace ...) - (In(Top) / In(ClassBody) / In(Block)) * T(Group) + In(Top, ClassBody, Block) * T(Group) << (T(Class) * T(Ident)[Ident] * ~T(Square)[TypeParams] * ~T(Type)[Type] * ~T(TypePred)[TypePred] * T(Brace)[ClassBody] * (Any++)[Rhs]) >> @@ -218,7 +210,7 @@ namespace verona << (Group << _[Rhs]); }, - (In(Top) / In(ClassBody) / In(Block)) * T(Class)[Class] << End >> + In(Top, ClassBody, Block) * T(Class)[Class] << End >> [](Match& _) { return err(_(Class), "Expected a `class` definition"); }, T(Class)[Class] << End >> [](Match& _) { @@ -236,7 +228,7 @@ namespace verona // Type structure. TypeStruct * T(Group)[Type] >> [](Match& _) { return Type << *_[Type]; }, - TypeStruct * (T(List) / T(Paren))[TypeTuple] >> + TypeStruct * T(List, Paren)[TypeTuple] >> [](Match& _) { return Type << (TypeTuple << *_[TypeTuple]); }, // Anonymous structural types. @@ -247,28 +239,38 @@ namespace verona }, // Strings in types are package descriptors. - TypeStruct * (T(String) / T(Escaped))[Package] >> + TypeStruct * T(String, Escaped)[Package] >> [](Match& _) { return Package << _(Package); }, TypeStruct * - (T(Equals) / T(Arrow) / T(Use) / T(Class) / T(TypeAlias) / T(Var) / - T(Let) / T(Ref) / T(If) / T(Else) / T(New) / T(Try) / - T(LLVMFuncType) / Literal)[Type] >> + (T(Equals, + Arrow, + Use, + Class, + TypeAlias, + Var, + Let, + Ref, + If, + Else, + New, + Try, + LLVMFuncType) / + Literal)[Type] >> [](Match& _) { return err(_(Type), "Can't put this in a type"); }, // A group can be in a Block, Expr, ExprSeq, Tuple, or Assign. - (In(Block) / In(Expr) / In(ExprSeq) / In(Tuple) / In(Assign)) * - T(Group)[Group] >> + In(Block, Expr, ExprSeq, Tuple, Assign) * T(Group)[Group] >> [](Match& _) { return Expr << *_[Group]; }, // An equals can be in a Block, ExprSeq, Tuple, or Expr. - (In(Block) / In(ExprSeq) / In(Tuple)) * T(Equals)[Equals] >> + In(Block, ExprSeq, Tuple) * T(Equals)[Equals] >> [](Match& _) { return Expr << (Assign << *_[Equals]); }, In(Expr) * T(Equals)[Equals] >> [](Match& _) { return Assign << *_[Equals]; }, // A list can be in a Block, ExprSeq, or Expr. - (In(Block) / In(ExprSeq)) * T(List)[List] >> + In(Block, ExprSeq) * T(List)[List] >> [](Match& _) { return Expr << (Tuple << *_[List]); }, In(Expr) * T(List)[List] >> [](Match& _) { return Tuple << *_[List]; }, @@ -308,7 +310,7 @@ namespace verona // Object literal. In(Expr) * T(New) * T(Brace)[ClassBody] >> [](Match& _) { - auto class_id = _.fresh(l_class); + auto class_id = _.fresh(l_objlit); return Seq << (Lift << Block << (Class << (Ident ^ class_id) << TypeParams << inherit() << typepred() @@ -334,8 +336,7 @@ namespace verona // (brace (list|group) (group arrow) ...) In(Expr) * (T(Brace) - << ((T(List) / T(Group))[Params] * (T(Group) << T(Arrow)) * - Any++[Rhs])) >> + << (T(List, Group)[Params] * (T(Group) << T(Arrow)) * Any++[Rhs])) >> [](Match& _) { return Lambda << TypeParams << (Params << _[Params]) << typevar(_) << typepred() << (Block << _[Rhs]); @@ -371,7 +372,7 @@ namespace verona [](Match& _) { return Expr << Ref << *_[Expr]; }, // Lift Use, Class, TypeAlias to Block. - In(Expr) * (T(Use) / T(Class) / T(TypeAlias))[Lift] >> + In(Expr) * T(Use, Class, TypeAlias)[Lift] >> [](Match& _) { return Lift << Block << _[Lift]; }, // A Type at the end of an Expr is a TypeAssert. A tuple is never directly @@ -381,9 +382,7 @@ namespace verona return Expr << (TypeAssert << (Expr << _[Expr]) << _(Type)); }, - In(Expr) * - (TypeCaps / T(TypePred) / T(Self) / T(Arrow) / - T(LLVMFuncType))[Expr] >> + In(Expr) * (TypeCaps / T(TypePred, Arrow, LLVMFuncType))[Expr] >> [](Match& _) { return err(_(Expr), "Can't put this in an expression"); }, diff --git a/src/passes/traitisect.cc b/src/passes/traitisect.cc index 3675a5ea..953acf73 100644 --- a/src/passes/traitisect.cc +++ b/src/passes/traitisect.cc @@ -10,21 +10,21 @@ namespace verona // late so that fields have already been turned into accessor functions and // partial application functions have already been generated. return { - dir::once | dir::topdown, + dir::bottomup | dir::once, { T(Trait)[Trait] << (T(Ident)[Ident] * T(ClassBody)[ClassBody]) >> [](Match& _) { // If we're inside a TypeIsect, put the new traits inside it. // Otherwise, create a new TypeIsect. Node r = - (_(Trait)->parent()->type() == TypeIsect) ? Seq : TypeIsect; + (_(Trait)->parent() == TypeIsect) ? Seq : TypeIsect; Node base = ClassBody; r << (Trait << _(Ident) << base); for (auto& member : *_(ClassBody)) { - if (member->type() == Function) + if (member == Function) { // Strip any default implementation. (member / Block) = DontCare; diff --git a/src/passes/typeinfer.cc b/src/passes/typeinfer.cc new file mode 100644 index 00000000..6144b06c --- /dev/null +++ b/src/passes/typeinfer.cc @@ -0,0 +1,186 @@ +// Copyright Microsoft and Project Verona Contributors. +// SPDX-License-Identifier: MIT +#include "../lang.h" +#include "../subtype.h" + +namespace verona +{ + Node gamma(Node node) + { + // TODO: Move vs Copy type. + // when testing, this may not resolve + return (node / Ident)->lookup().front() / Type; + } + + struct Infer + { + Bounds& bounds; + Node cls; + Btype ret_type; + Btype bool_type; + + Infer(Node f, Bounds& b) : bounds(b) + { + cls = f->parent(Class); + ret_type = make_btype(f / Type); + bool_type = make_btype(booltype()); + check(do_block(f / Block), ret_type); + } + + void check(Btype ltype, Btype rtype) + { + if (!subtype(ltype, rtype, bounds)) + { + // TODO: + std::cout << "subtype failed" << std::endl + << "---" << std::endl + << ltype << "---" << std::endl + << rtype; + } + } + + void check(Node ltype, Btype rtype) + { + if (ltype) + check(make_btype(ltype), rtype); + } + + Node do_block(Node& block) + { + for (auto stmt : *block) + { + if (stmt == Bind) + { + // TODO: literals + auto rhs = stmt / Rhs; + auto type = make_btype(stmt / Type); + + if (rhs == TypeTest) + check(bool_type, type); + else if (rhs == Cast) + check(rhs / Type, type); + else if (rhs->in({Move, Copy})) + check(gamma(rhs), type); + else if (rhs == FieldRef) + { + // TODO: Self substitution? + // this is failing when `type` is a TypeVar, unclear why + check( + TypeView << -gamma(rhs / Ref) + << reftype( + cls->lookdown((rhs / Ident)->location()).front() / + Type), + type); + } + else if (rhs == Conditional) + { + check(gamma(rhs / If), bool_type); + check(do_block(rhs / True), type); + check(do_block(rhs / False), type); + } + else if (rhs == Call) + { + auto sel = rhs / Selector; + + if (sel == FQFunction) + { + // TODO: may not have a function of this arity + auto l = resolve_fq(sel); + auto params = clone(l.def / Params); + auto ret = clone(l.def / Type); + auto args = rhs / Args; + l.sub(params); + l.sub(ret); + + (void)std::equal( + params->begin(), + params->end(), + args->begin(), + args->end(), + [&](Node& param, Node& arg) { + check(gamma(arg), make_btype(param / Type)); + return true; + }); + + check(ret, type); + } + else + { + // TODO: selector + } + } + else if (rhs->in( + {Int, + Bin, + Oct, + Hex, + Float, + HexFloat, + Char, + Escaped, + String, + Tuple, + LLVM})) + { + // TODO: + } + else + { + assert(false); + } + } + else if (stmt == Return) + { + assert(stmt == block->back()); + check(gamma(stmt / Ref), ret_type); + } + else if (stmt == Move) + { + assert(stmt == block->back()); + return gamma(stmt); + } + else + { + assert(stmt->in({Class, TypeAlias, LLVM, Drop})); + } + } + + return {}; + } + }; + + PassDef typeinfer() + { + auto bounds = std::make_shared(); + + PassDef typeinfer = { + dir::bottomup | dir::once, + { + T(Function)[Function] >> ([=](Match& _) -> Node { + Infer infer(_(Function), *bounds); + return NoChange; + }), + }}; + + typeinfer.post([=](Node) { + for (auto& [typevar, bound] : *bounds) + { + std::cout << typevar.view() << std::endl << "--lower--" << std::endl; + + for (auto& b : bound.lower) + std::cout << b << std::endl; + + std::cout << "--upper--" << std::endl; + + for (auto& b : bound.upper) + std::cout << b << std::endl; + + std::cout << "--done--" << std::endl; + } + + return 0; + }); + + return typeinfer; + } +} diff --git a/src/passes/typenames.cc b/src/passes/typenames.cc index 07a3f943..1268a3d7 100644 --- a/src/passes/typenames.cc +++ b/src/passes/typenames.cc @@ -32,7 +32,7 @@ namespace verona auto fq = make_fq(def); - if (fq->type() != FQType) + if (fq != FQType) return Error << (ErrorMsg ^ "type name is not a type") << ((ErrorAst ^ id) << id << ta); diff --git a/src/passes/typereference.cc b/src/passes/typereference.cc new file mode 100644 index 00000000..9a040032 --- /dev/null +++ b/src/passes/typereference.cc @@ -0,0 +1,124 @@ +// Copyright Microsoft and Project Verona Contributors. +// SPDX-License-Identifier: MIT +#include "../lang.h" +#include "../lookup.h" + +namespace verona +{ + PassDef typereference() + { + return { + // Unscoped reference. + In(Expr) * T(Ident, Symbol, Self)[Ident] * ~T(TypeArgs)[TypeArgs] >> + [](Match& _) { + auto id = _(Ident); + auto ta = _(TypeArgs); + auto defs = lookup(id, ta); + + if (defs.size() == 1) + { + auto def = *defs.begin(); + + if (def.too_many_typeargs) + { + return Error << (ErrorMsg ^ "too many type arguments") + << ((ErrorAst ^ id) << id << ta); + } + + auto fq = make_fq(def); + + if (fq == FQType) + return fq; + } + + if (defs.size() > 1) + { + // If there are multiple definitions, it's an ambiguous reference. + auto err = Error << (ErrorMsg ^ "ambiguous reference") + << ((ErrorAst ^ id) << id << ta); + + for (auto& other : defs) + err << (ErrorAst ^ (other.def / Ident)); + + return err; + } + + // If there isn't a single type definition, treat it as a selector. + return selector(id, ta); + }, + + // Scoped reference. + In(Expr) * T(FQType)[Lhs] * T(DoubleColon) * T(Selector)[Selector] >> + [](Match& _) { + auto sel = _(Selector); + auto id = sel / Ident; + auto ta = sel / TypeArgs; + auto def = resolve_fq(_(Lhs)); + auto defs = lookdown(def, id, ta); + + if (defs.size() == 0) + return Error << (ErrorMsg ^ "unknown reference") + << ((ErrorAst ^ id) << id << ta); + + if (defs.size() == 1) + { + auto ldef = *defs.begin(); + + if (ldef.too_many_typeargs) + { + return Error << (ErrorMsg ^ "too many type arguments") + << ((ErrorAst ^ id) << id << ta); + } + + return make_fq(ldef); + } + + if (std::any_of(defs.begin(), defs.end(), [](auto& d) { + return d.def != Function; + })) + { + // If there are multiple definitions, and at least one of them is + // not a function, then we have an ambiguous reference. + auto err = Error << (ErrorMsg ^ "ambiguous reference") + << ((ErrorAst ^ id) << id << ta); + + for (auto& other : defs) + err << (ErrorAst ^ (other.def / Ident)); + + return err; + } + + // Select the smallest arity function. + auto l = + *std::min_element(defs.begin(), defs.end(), [](auto& a, auto& b) { + return (a.def / Params)->size() < (b.def / Params)->size(); + }); + + return make_fq(l); + }, + + // Error out on invalid scoped references. + In(Expr) * T(DoubleColon)[DoubleColon] >> + [](Match& _) { + return err(_(DoubleColon), "Expected a scoped reference"); + }, + + // Lone TypeArgs are typeargs on apply. + In(Expr) * T(TypeArgs)[TypeArgs] >> + [](Match& _) { return Seq << Dot << selector(l_apply, _(TypeArgs)); }, + + // Create sugar, with no arguments. + In(Expr) * T(FQType)[FQType] * ~T(TypeArgs)[TypeArgs] >> + [](Match& _) { + return append_fq(_(FQType), selector(l_create, _(TypeArgs))); + }, + + // New sugar. + In(Expr) * T(New)[New] >> + [](Match& _) { + return append_fq( + local_fq(_(New)->parent({Class, Trait})), selector(l_new)); + }, + }; + } +} diff --git a/src/passes/typevalid.cc b/src/passes/typevalid.cc index 5c0176a4..9eb1d672 100644 --- a/src/passes/typevalid.cc +++ b/src/passes/typevalid.cc @@ -8,7 +8,7 @@ namespace verona { // This detects cycles in type aliases, which are not allowed. This happens // after type names are turned into FQType. - assert(node->type() == TypeAlias); + assert(node == TypeAlias); // Each element in the worklist carries a set of nodes that have been // visited, a type node, and a map of typeparam bindings. @@ -22,19 +22,16 @@ namespace verona auto& lookup = work.second; worklist.pop_back(); - if (lookup.def->type() == Type) + if (lookup.def == Type) { worklist.emplace_back(set, lookup.make(lookup.def / Type)); } - else if (lookup.def->type().in( - {TypeTuple, TypeUnion, TypeIsect, TypeView})) + else if (lookup.def->in({TypeTuple, TypeUnion, TypeIsect, TypeView})) { for (auto& t : *lookup.def) worklist.emplace_back(set, lookup.make(t)); } - else if ( - (lookup.def->type() == FQType) && - ((lookup.def / Type)->type() == TypeAliasName)) + else if ((lookup.def == FQType) && ((lookup.def / Type) == TypeAliasName)) { auto l = resolve_fq(lookup.def); @@ -47,9 +44,7 @@ namespace verona worklist.emplace_back(set, l); } } - else if ( - (lookup.def->type() == FQType) && - ((lookup.def / Type)->type() == TypeParamName)) + else if ((lookup.def == FQType) && ((lookup.def / Type) == TypeParamName)) { auto l = resolve_fq(lookup.def); @@ -68,7 +63,7 @@ namespace verona bool recursive_inherit(Node node) { - assert(node->type() == Inherit); + assert(node == Inherit); std::vector> worklist; worklist.emplace_back(NodeSet{node}, node / Inherit); @@ -79,18 +74,16 @@ namespace verona auto& lookup = work.second; worklist.pop_back(); - if (lookup.def->type() == Type) + if (lookup.def == Type) { worklist.emplace_back(set, lookup.make(lookup.def / Type)); } - else if (lookup.def->type() == TypeIsect) + else if (lookup.def == TypeIsect) { for (auto& t : *lookup.def) worklist.emplace_back(set, lookup.make(t)); } - else if ( - (lookup.def->type() == FQType) && - ((lookup.def / Type)->type() == TypeClassName)) + else if ((lookup.def == FQType) && ((lookup.def / Type) == TypeClassName)) { auto l = resolve_fq(lookup.def); @@ -98,25 +91,21 @@ namespace verona { Node inherit = l.def / Inherit; - if ((inherit->type() != Inherit) || set.contains(inherit)) + if ((inherit != Inherit) || set.contains(inherit)) return true; set.insert(inherit); worklist.emplace_back(set, inherit / Inherit); } } - else if ( - (lookup.def->type() == FQType) && - ((lookup.def / Type)->type() == TypeAliasName)) + else if ((lookup.def == FQType) && ((lookup.def / Type) == TypeAliasName)) { auto l = resolve_fq(lookup.def); if (l.def) worklist.emplace_back(set, l); } - else if ( - (lookup.def->type() == FQType) && - ((lookup.def / Type)->type() == TypeParamName)) + else if ((lookup.def == FQType) && ((lookup.def / Type) == TypeParamName)) { auto l = resolve_fq(lookup.def); @@ -140,7 +129,7 @@ namespace verona // that expand to predicates. Btype t = make_btype(fq); - if (t->type() != TypeAlias) + if (t != TypeAlias) return false; Btypes worklist; @@ -151,20 +140,20 @@ namespace verona t = worklist.back(); worklist.pop_back(); - if (t->type() == TypeSubtype) + if (t == TypeSubtype) { // Do nothing. } - else if (t->type().in({TypeUnion, TypeIsect})) + else if (t->in({TypeUnion, TypeIsect})) { // Check that all children are valid predicates. std::for_each(t->node->begin(), t->node->end(), [&](auto& n) { worklist.push_back(t->make(n)); }); } - else if (t->type() == TypeAlias) + else if (t == TypeAlias) { - worklist.push_back(t->field(Type)); + worklist.push_back(t / Type); } else { @@ -182,7 +171,7 @@ namespace verona // valid inherit clauses. Btype t = make_btype(fq); - if (!t->type().in({Class, Trait, TypeAlias})) + if (!t->in({Class, Trait, TypeAlias})) return false; Btypes worklist; @@ -193,20 +182,20 @@ namespace verona t = worklist.back(); worklist.pop_back(); - if (t->type().in({Class, Trait})) + if (t->in({Class, Trait})) { // Do nothing. } - else if (t->type().in({Type, TypeIsect})) + else if (t->in({Type, TypeIsect})) { // Check that all children are valid for code reuse. std::for_each(t->node->begin(), t->node->end(), [&](auto& n) { worklist.push_back(t->make(n)); }); } - else if (t->type() == TypeAlias) + else if (t == TypeAlias) { - worklist.push_back(t->field(Type)); + worklist.push_back(t / Type); } else { @@ -220,7 +209,7 @@ namespace verona PassDef typevalid() { return { - dir::once | dir::topdown, + dir::bottomup | dir::once, { T(TypeAlias)[TypeAlias] >> ([](Match& _) -> Node { if (recursive_typealias(_(TypeAlias))) @@ -245,8 +234,9 @@ namespace verona }), In(TypePred)++ * --(In(TypeSubtype, TypeArgs)++) * - (TypeCaps / T(FQType) / T(Trait) / T(TypeTuple) / T(Self) / - T(TypeList) / T(TypeView) / T(TypeVar) / T(Package))[Type] >> + (TypeCaps / + T(Trait, TypeTuple, Self, TypeList, TypeView, TypeVar, Package)) + [Type] >> [](Match& _) { return err(_(Type), "Can't put this in a type predicate"); }, @@ -260,9 +250,17 @@ namespace verona }), In(Inherit)++ * --(In(TypeArgs)++) * - (TypeCaps / T(TypeTuple) / T(Self) / T(TypeList) / T(TypeView) / - T(TypeUnion) / T(TypeVar) / T(Package) / T(TypeSubtype) / - T(TypeTrue) / T(TypeFalse))[Type] >> + (TypeCaps / + T(TypeTuple, + Self, + TypeList, + TypeView, + TypeUnion, + TypeVar, + Package, + TypeSubtype, + TypeTrue, + TypeFalse))[Type] >> [](Match& _) { return err(_(Type), "Can't inherit from this type"); }, }}; } diff --git a/src/passes/validtypeargs.cc b/src/passes/validtypeargs.cc index 7f9ceaf2..bd535643 100644 --- a/src/passes/validtypeargs.cc +++ b/src/passes/validtypeargs.cc @@ -10,7 +10,7 @@ namespace verona return { dir::bottomup | dir::once, { - (T(FQType) / T(FQFunction))[Type] >> ([](Match& _) -> Node { + T(FQType, FQFunction)[Type] >> ([](Match& _) -> Node { auto tn = _(Type); if (is_implicit(tn)) @@ -21,10 +21,10 @@ namespace verona // Ignore TypeParams and TypeTraits, as they don't have predicates. // If this fails to resolve to a definition, ignore it. It's either // test code, or the LHS has an error. - if (!bt->type().in({Class, TypeAlias, Function})) + if (!bt->in({Class, TypeAlias, Function})) return NoChange; - if (!subtype(make_btype(TypeTrue), bt->field(TypePred))) + if (!subtype(make_btype(TypeTrue), bt / TypePred)) return err(tn, "Invalid type arguments"); return NoChange; diff --git a/src/readme.md b/src/readme.md index 3535983c..88080dd5 100644 --- a/src/readme.md +++ b/src/readme.md @@ -1,12 +1,20 @@ # To Do -- Lookup in a `TypeParam`, e.g., `create` or an associated type. +- Blocks. + - Enforce `Return | Move | LLVM` at the end? +- Type inference. + - Uses of `typevar`: + - Return type of `new`, auto-create, lambda create, lambda apply, partial functions, partial app class create, partial app class apply. + - Lambda free variable types. + - `DontCare` syntactically in a type (remove this?). + - Unspecified field types, parameter types, function return types. - Free variables in object literals. - Tuples as traits. - Pattern matching. -- Type inference. +- Type lists. - `lazy[T]` - `weak[T]` +- Lookup in a `TypeParam`, e.g., `create` or an associated type. - Public/private. - Package schemes. - Better system for including parts of `std`. @@ -26,78 +34,79 @@ Use `where` for conditional compilation: ## Tuples as Traits -Tuples are traits: +Tuples as traits: ```ts -type Tuple[T, U] = -{ - head(self): self.T - rest(self): self.U -} - -// make unit a 0-arity tuple -class Unit: Tuple[(), ()] -{ - head(self): () = () - rest(self): () = () -} - -Unit: Tuple[Unit, Unit] -T1: Tuple[T1, Unit] -(T1, T2): Tuple[T1, Tuple[T2, Unit]] -(T1, T2, T3): Tuple[T1, Tuple[T2, Tuple[T3, Unit]]] -(T1, T2, T3, T4): Tuple[T1, Tuple[T2, Tuple[T3, Tuple[T4, Unit]]]] - match w { // matches Unit - { () => e0 } - // matches {} (x = w) - { x => e1 } - // matches tuple_1 (x = w._0, y = w._1plus) - // problem: for w: (T1, T2), we want y: T2, not y: Tuple[T2, Unit] - { x, y => e2 } - // matches tuple_2 (x = _0, y = _1, z = _2plus) - { x, y, z => e3 } - // explicity indicate a w._1plus match? - { x, y... => e2plus } + { () => e } + // matches Any (x = w) + { x => e } + // matches tuple_2 (x = w._0, y = w._1) + { x, y => e } + // matches tuple_3 (x = w._0, y = w._1, z = w._3) + { x, y, z => e } + // explicity indicate a tuple_1+ match + // x = w._0, y = w._1plus + { x, y... => e } } +x, y... = e // e must be tuple_1 +x, y = e // e must be tuple_2 +x, y, z... = e // e must be tuple_2 +x, y, z = e // e must be tuple_3 + // experiment: tuple types -class tuple_1[T1] +class unit +{ + _0(self): unit = self + _1plus(self): unit = self +} + +type ituple_1 = { size(self): Size = 1 - apply(self, n: Size): T1 | Unit = if (n == 0) { self._0 } - _0(self): T1 + apply(self, n: Size): Self | () = if (n == 0) { self } + _0(self): Self = self + _1plus(self): () } -class tuple_2[T1, T2] +type ituple_2[T1, T2] = { + let _0: T1 + let _1: T2 size(self): Size = 2 - apply(self, n: Size): T1 | T2 | Unit = + apply(self, n: Size): T1 | T2 | () = if (n == 0) { self._0 } else if (n == 1) { self._1 } - - _0(self): T1 - _1(self): T2 + _1plus(self): (T2, ()) + _2plus(self): () } -class tuple_3[T1, T2, T3] +class tuple_2[T1, T2]: ituple[T1, T2] {} + +type ituple_3[T1, T2, T3] = { - size(self): Size = 2 - apply(self, n: Size): T1 | T2 | T3 | Unit = + let _0: T1 + let _1: T2 + let _2: T3 + size(self): Size = 3 + apply(self, n: Size): T1 | T2 | T3 | () = if (n == 0) { self._0 } else if (n == 1) { self._1 } else if (n == 2) { self._2 } - - _0(self): T1 - _1(self): T2 - _2(self): T3 + _1plus(self): (T2, T3, ()) + _2plus(self): (T3, ()) + _3plus(self): () } +class tuple_3[T1, T2, T3]: ituple[T1, T2, T3] {} + type typelist[T] = { size(self): Size - apply(self, n: Size): T | Unit + apply(self, n: Size): T | () + rest(self, n: Size): typelist[T] } ``` diff --git a/src/subtype.cc b/src/subtype.cc index ac1b225c..7c054794 100644 --- a/src/subtype.cc +++ b/src/subtype.cc @@ -6,6 +6,27 @@ namespace verona { + void merge(Bounds& lhs, Bounds& rhs) + { + for (auto& [k, v] : rhs) + { + auto it = lhs.find(k); + + if (it == lhs.end()) + { + lhs[k] = v; + } + else + { + // TODO: subsume bounds? + it->second.lower.insert( + it->second.lower.end(), v.lower.begin(), v.lower.end()); + it->second.upper.insert( + it->second.upper.end(), v.upper.begin(), v.upper.end()); + } + } + } + struct Assume { Btype sub; @@ -13,8 +34,8 @@ namespace verona Assume(Btype sub, Btype sup) : sub(sub), sup(sup) { - assert(sub->type().in({Class, Trait})); - assert(sup->type() == Trait); + assert(sub->in({Class, Trait})); + assert(sup == Trait); } }; @@ -27,6 +48,7 @@ namespace verona Btypes self; Btypes predicates; std::vector assumptions; + Bounds bounds; Sequent() = default; @@ -37,7 +59,8 @@ namespace verona rhs_atomic(rhs.rhs_atomic), self(rhs.self), predicates(rhs.predicates), - assumptions(rhs.assumptions) + assumptions(rhs.assumptions), + bounds(rhs.bounds) {} void push_assume(Btype sub, Btype sup) @@ -52,7 +75,7 @@ namespace verona void push_self(Btype s) { - assert(s->type() == Class); + assert(s == Class); self.push_back(s); } @@ -67,7 +90,7 @@ namespace verona while (p) { - if (p->type().in({Function, Class, TypeAlias})) + if (p->in({Function, Class, TypeAlias})) { auto pred = p / TypePred; @@ -81,6 +104,8 @@ namespace verona bool reduce(Btype l, Btype r) { + // Start a fresh reduction, keeping the existing Self binding, predicates, + // and assumptions. Sequent seq; seq.lhs_pending.push_back(l); seq.rhs_pending.push_back(r); @@ -89,7 +114,36 @@ namespace verona seq.assumptions = assumptions; seq.add_predicates(l); seq.add_predicates(r); - return seq.reduce(); + + if (!seq.reduce()) + return false; + + merge(bounds, seq.bounds); + return true; + } + + bool lhs_reduce(Btype t) + { + Sequent seq(*this); + seq.lhs_pending.push_back(t); + + if (!seq.reduce()) + return false; + + merge(bounds, seq.bounds); + return true; + } + + bool rhs_reduce(Btype t) + { + Sequent seq(*this); + seq.rhs_pending.push_back(t); + + if (!seq.reduce()) + return false; + + merge(bounds, seq.bounds); + return true; } bool reduce() @@ -99,7 +153,7 @@ namespace verona auto r = rhs_pending.back(); rhs_pending.pop_back(); - if (r->type() == TypeUnion) + if (r == TypeUnion) { // Π ⊩ Γ ⊢ Δ, A, B // --- @@ -109,7 +163,7 @@ namespace verona for (auto& t : *r->node) rhs_pending.push_back(r->make(t)); } - else if (r->type() == TypeIsect) + else if (r == TypeIsect) { // Π ⊩ Γ ⊢ Δ, A // Π ⊩ Γ ⊢ Δ, B @@ -119,29 +173,23 @@ namespace verona // RHS isect is a sequent split. for (auto& t : *r->node) { - Sequent seq(*this); - seq.rhs_pending.push_back(r->make(t)); - - if (!seq.reduce()) + if (!rhs_reduce(r->make(t))) return false; } return true; } - else if (r->type() == TypeAlias) + else if (r == TypeAlias) { // Demand that we satisfy the type predicate, which is a split. - Sequent seq(*this); - seq.rhs_pending.push_back(r->field(TypePred)); - - if (!seq.reduce()) + if (!rhs_reduce(r / TypePred)) return false; // Try both the typealias and the underlying type. - rhs_pending.push_back(r->field(Type)); + rhs_pending.push_back(r / Type); rhs_atomic.push_back(r); } - else if (r->type() == TypeView) + else if (r == TypeView) { auto [rr, done] = reduce_view(r); @@ -150,7 +198,7 @@ namespace verona else rhs_pending.push_back(rr); } - else if (r->type() == Self) + else if (r == Self) { // Try both Self and the current self type. rhs_atomic.push_back(r); @@ -169,7 +217,7 @@ namespace verona auto l = lhs_pending.back(); lhs_pending.pop_back(); - if (l->type() == TypeSubtype) + if (l == TypeSubtype) { // Π, A < B ⊩ Γ ⊢ Δ, A // Π, A < B ⊩ Γ, B ⊢ Δ @@ -177,15 +225,12 @@ namespace verona // Π ⊩ Γ, A < B ⊢ Δ predicates.push_back(l); - Sequent seq(*this); - seq.rhs_pending.push_back(l->field(Lhs)); - - if (!seq.reduce()) + if (!rhs_reduce(l / Lhs)) return false; - lhs_pending.push_back(l->field(Rhs)); + lhs_pending.push_back(l / Rhs); } - else if (l->type() == TypeIsect) + else if (l == TypeIsect) { // Γ, A, B ⊢ Δ // --- @@ -195,7 +240,7 @@ namespace verona for (auto& t : *l->node) lhs_pending.push_back(l->make(t)); } - else if (l->type() == TypeUnion) + else if (l == TypeUnion) { // Γ, A ⊢ Δ // Γ, B ⊢ Δ @@ -205,25 +250,22 @@ namespace verona // LHS union is a sequent split. for (auto& t : *l->node) { - Sequent seq(*this); - seq.lhs_pending.push_back(l->make(t)); - - if (!seq.reduce()) + if (!lhs_reduce(l->make(t))) return false; } return true; } - else if (l->type() == TypeAlias) + else if (l == TypeAlias) { // Assume that we've satisfied the type predicate. - lhs_pending.push_back(l->field(TypePred)); + lhs_pending.push_back(l / TypePred); // Try both the typealias and the underlying type. - lhs_pending.push_back(l->field(Type)); + lhs_pending.push_back(l / Type); lhs_atomic.push_back(l); } - else if (l->type() == TypeView) + else if (l == TypeView) { auto [ll, done] = reduce_view(l); @@ -232,7 +274,7 @@ namespace verona else rhs_pending.push_back(ll); } - else if (l->type() == Self) + else if (l == Self) { // Try both Self and the current self type. lhs_atomic.push_back(l); @@ -273,27 +315,27 @@ namespace verona bool subtype_one(Btype& l, Btype& r) { // TypeFalse is a subtype of everything. - if (l->type() == TypeFalse) + if (l == TypeFalse) return true; // Everything is a subtype of TypeTrue. - if (r->type() == TypeTrue) + if (r == TypeTrue) return true; // Skip TypeVar on either side. - if ((l->type() == TypeVar) || (r->type() == TypeVar)) + if ((l == TypeVar) || (r == TypeVar)) return false; // These must be the same type. // TODO: region tracking - if (r->type().in({Iso, Mut, Imm, Self})) + if (r->in({Iso, Mut, Imm, Self})) return l->type() == r->type(); // Tuples must be the same arity and each element must be a subtype. // TODO: remove TypeTuple from the language, use a trait - if (r->type() == TypeTuple) + if (r == TypeTuple) { - return (l->type() == TypeTuple) && + return (l == TypeTuple) && std::equal( l->node->begin(), l->node->end(), @@ -307,43 +349,49 @@ namespace verona // Nothing is a subtype of a TypeList. Two TypeLists may have // different instantiated arity, even if they have the same bounds. // Use a TypeParam with a TypeList upper bounds to get subtyping. - if (r->type() == TypeList) + if (r == TypeList) return false; // Check for the same definition site. - if (r->type() == TypeParam) + if (r == TypeParam) return same_def_site(l, r); // Check for the same definition site with invariant typeargs. - if (r->type().in({TypeAlias, Class})) + if (r->in({TypeAlias, Class})) return same_def_site(l, r) && invariant_typeargs(l, r); // A package resolves to a class. Once we have package resolution, // compare the classes, as different strings could resolve to the // same package. - if (r->type() == Package) + if (r == Package) { - return (l->type() == Package) && + return (l == Package) && ((l->node / Ident)->location() == (r->node / Ident)->location()); } // Check predicate subtyping. - if (r->type() == TypeSubtype) + if (r == TypeSubtype) { // ⊩ Π, A ⊢ B // --- // Π ⊩ Γ ⊢ Δ, A < B Sequent seq; seq.lhs_pending = predicates; - seq.lhs_pending.push_back(r->field(Lhs)); - seq.rhs_pending.push_back(r->field(Rhs)); - return seq.reduce(); + seq.lhs_pending.push_back(r / Lhs); + seq.rhs_pending.push_back(r / Rhs); + seq.bounds = bounds; + + if (!seq.reduce()) + return false; + + merge(bounds, seq.bounds); + return true; } // Check structural subtyping. - if (r->type() == Trait) + if (r == Trait) { - if (!l->type().in({Class, Trait})) + if (!l->in({Class, Trait})) return false; // If any assumption is true, the trait is satisfied. @@ -361,7 +409,7 @@ namespace verona push_assume(l, r); - if (l->type() == Class) + if (l == Class) push_self(l); bool ok = true; @@ -369,7 +417,7 @@ namespace verona for (auto rf : *rbody) { - if (rf->type() != Function) + if (rf != Function) continue; // At this point, traits have been decomposed into intersections of @@ -377,11 +425,9 @@ namespace verona auto id = (rf / Ident)->location(); auto arity = (rf / Params)->size(); auto lfs = l->node->lookdown(id); - auto it = std::find_if( - lfs.begin(), lfs.end(), [&](auto& lf) { - return (lf->type() == Function) && - ((lf / Params)->size() == arity); - }); + auto it = std::find_if(lfs.begin(), lfs.end(), [&](auto& lf) { + return (lf == Function) && ((lf / Params)->size() == arity); + }); if (it == lfs.end()) { @@ -433,14 +479,14 @@ namespace verona // TODO: If the check succeeded, memoize it. pop_assume(); - if (l->type() == Class) + if (l == Class) pop_self(); return ok; } // TODO: handle viewpoint adaptation - if (r->type() == TypeView) + if (r == TypeView) { // TODO: the end of a TypeView can be a TypeParam. If it is, we need to // be able to use that to fulfill Class / Trait / etc if the TypeView is @@ -456,15 +502,17 @@ namespace verona { bool ok = false; - if (l->type() == TypeVar) + if (l == TypeVar) { // TODO: l.upper += r + bounds[l->node->location()].upper.push_back(r); ok = true; } - if (r->type() == TypeVar) + if (r == TypeVar) { // TODO: r.lower += l + bounds[r->node->location()].lower.push_back(l); ok = true; } @@ -484,7 +532,7 @@ namespace verona while (node) { - if (node->type().in({Class, TypeAlias, Function})) + if (node->in({Class, TypeAlias, Function})) { for (auto& tp : *(node / TypeParams)) { @@ -504,7 +552,7 @@ namespace verona std::pair reduce_view(Btype& t) { - assert(t->type() == TypeView); + assert(t == TypeView); auto start = t->node->begin(); auto end = t->node->end(); @@ -514,8 +562,7 @@ namespace verona auto rhs = NodeRange{it + 1, end}; auto r = t->make(*it); - if (r->type().in( - {Package, Class, Trait, TypeTuple, TypeTrue, TypeFalse})) + if (r->in({Package, Class, Trait, TypeTuple, TypeTrue, TypeFalse})) { // The viewpoint path can be discarded. if (*it == t->node->back()) @@ -524,7 +571,7 @@ namespace verona // There is no view through this type, so treat it as true, i.e. top. return {t->make(TypeTrue), false}; } - else if (r->type() == TypeList) + else if (r == TypeList) { // A.(B...) = (A.B)... if (*it == t->node->back()) @@ -535,7 +582,7 @@ namespace verona // There is no view through this type, so treat it as true, i.e. top. return {t->make(TypeTrue), false}; } - else if (r->type().in({TypeUnion, TypeIsect})) + else if (r->in({TypeUnion, TypeIsect})) { // A.(B | C).D = A.B.D | A.C.D // A.(B & C).D = A.B.D & A.C.D @@ -546,12 +593,12 @@ namespace verona return {r->make(node), false}; } - else if (r->type() == TypeAlias) + else if (r == TypeAlias) { return { - r->field(Type)->make(TypeView << -lhs << -r->node << -rhs), false}; + (r / Type)->make(TypeView << -lhs << -r->node << -rhs), false}; } - else if (r->type() == TypeView) + else if (r == TypeView) { // A.(B.C).D = A.B.C.D auto node = TypeView << -lhs; @@ -572,6 +619,7 @@ namespace verona auto r = t->make(*it); // If any step in the view is Imm, the whole view is Imm. + // TODO: if r is a TypeVar, this will bind it to `imm` and succeed. if (reduce(r, t_imm)) { if (*it == t->node->back()) @@ -586,15 +634,28 @@ namespace verona } }; - bool subtype(Node sub, Node sup) - { - Sequent seq; - return seq.reduce(make_btype(sub), make_btype(sup)); - } - bool subtype(Btype sub, Btype sup) { Sequent seq; - return seq.reduce(sub, sup); + seq.lhs_pending.push_back(sub); + seq.rhs_pending.push_back(sup); + seq.add_predicates(sub); + seq.add_predicates(sub); + return seq.reduce(); + } + + bool subtype(Btype sub, Btype sup, Bounds& bounds) + { + Sequent seq; + seq.lhs_pending.push_back(sub); + seq.rhs_pending.push_back(sup); + seq.add_predicates(sub); + seq.add_predicates(sub); + + if (!seq.reduce()) + return false; + + merge(bounds, seq.bounds); + return true; } } diff --git a/src/subtype.h b/src/subtype.h index 7028dba0..ef1b8ac5 100644 --- a/src/subtype.h +++ b/src/subtype.h @@ -12,6 +12,14 @@ namespace verona { using namespace trieste; - bool subtype(Node sub, Node sup); + struct Bound + { + std::vector lower; + std::vector upper; + }; + + using Bounds = std::map; + bool subtype(Btype sub, Btype sup); + bool subtype(Btype sub, Btype sup, Bounds& bounds); } diff --git a/src/wf.h b/src/wf.h index cd9c7b91..cfa964c8 100644 --- a/src/wf.h +++ b/src/wf.h @@ -8,14 +8,14 @@ namespace verona { using namespace wf::ops; - inline const auto wfImplicit = Implicit >>= Implicit | Explicit; + inline const auto wfImplicit = Implicit >>= Implicit | Explicit | LambdaFunc; inline const auto wfHand = Ref >>= Lhs | Rhs; - inline const auto wfParserTokens = Bool | Int | Hex | Bin | Float | HexFloat | - Char | Escaped | String | LLVM | Iso | Mut | Imm | Brace | Paren | Square | - List | Equals | Arrow | Use | Class | TypeAlias | Where | Var | Let | Ref | - Self | If | Else | New | Try | DontCare | Ident | Ellipsis | Dot | Colon | - DoubleColon | TripleColon | Symbol; + inline const auto wfParserTokens = True | False | Int | Hex | Oct | Bin | + Float | HexFloat | Char | Escaped | String | LLVM | Iso | Mut | Imm | + Brace | Paren | Square | List | Equals | Arrow | Use | Class | TypeAlias | + Where | Var | Let | Ref | Self | If | Else | New | Try | DontCare | Ident | + Ellipsis | Dot | Colon | DoubleColon | TripleColon | Symbol; // clang-format off inline const auto wfParser = @@ -56,11 +56,12 @@ namespace verona Ellipsis | Ident | Symbol | Dot | DoubleColon; inline const auto wfExprStructure = Expr | ExprSeq | Unit | Tuple | Assign | - TypeArgs | If | Else | Lambda | Let | Var | New | Try | Ref | DontCare | - Ellipsis | Dot | Ident | Symbol | DoubleColon | Bool | Int | Hex | Bin | - Float | HexFloat | Char | Escaped | String | LLVM | TypeAssert; + TypeArgs | Self | If | Else | Lambda | Let | Var | New | Try | Ref | + DontCare | Ellipsis | Dot | Ident | Symbol | DoubleColon | True | False | + Int | Hex | Oct | Bin | Float | HexFloat | Char | Escaped | String | LLVM | + TypeAssert; - inline const auto wfDefault = Default >>= Lambda | DontCare; + inline const auto wfDefault = Default >>= Block | DontCare; // clang-format off inline const auto wfPassStructure = @@ -102,13 +103,60 @@ namespace verona ; // clang-format on + // Add RefVar, RefLet, Selector, FQFunction. + inline const auto wfExprReference = + wfExprStructure | RefVar | RefLet | Selector; + + // clang-format off + inline const auto wfPassReference = + wfPassStructure + | (RefLet <<= Ident) + | (RefVar <<= Ident) + | (Selector <<= wfHand * Ident * Int * TypeArgs) + | (Expr <<= wfExprReference++[1]) + ; + // clang-format on + + // Remove If, Else. Add Conditional, TypeTest, Cast. + inline const auto wfExprConditionals = + (wfExprReference - (If | Else)) | Conditional | TypeTest | Cast; + + // clang-format off + inline const auto wfPassConditionals = + wfPassReference + | (Conditional <<= (If >>= Expr) * Block * Block) + | (TypeTest <<= Expr * Type) + | (Cast <<= Expr * Type) + | (Expr <<= wfExprConditionals++[1]) + ; + // clang-format on + + // Remove Lambda. + inline const auto wfExprLambda = wfExprConditionals - Lambda; + + // clang-format off + inline const auto wfPassLambda = + wfPassConditionals + | (Expr <<= wfExprLambda++[1]) + ; + // clang-format on + + // clang-format off + inline const auto wfPassDefaultArgs = + wfPassLambda + | (FieldLet <<= wfImplicit * Ident * Type)[Ident] + | (FieldVar <<= wfImplicit * Ident * Type)[Ident] + | (Param <<= Ident * Type)[Ident] + ; + // clang-format on + // Remove DontCare, Ident. Add FQType. inline const auto wfTypeNames = (wfTypeStructure - (DontCare | Ident)) | FQType; // clang-format off inline const auto wfPassTypeNames = - wfPassStructure + wfPassDefaultArgs | (FQType <<= TypePath * (Type >>= @@ -120,7 +168,6 @@ namespace verona | (TypeAliasName <<= Ident * TypeArgs) | (TypeParamName <<= Ident) | (TypeTraitName <<= Ident) - | (Selector <<= wfHand * Ident * Int * TypeArgs) | (Type <<= wfTypeNames++) ; // clang-format on @@ -162,7 +209,7 @@ namespace verona ; // clang-format on - // Remove Type. + // Remove Type. Types are no longer sequences. inline const auto wfType = wfTypeAlg - Type; // clang-format off @@ -174,69 +221,52 @@ namespace verona | (TypeSubtype <<= (Lhs >>= wfType) * (Rhs >>= wfType)) | (TypeUnion <<= (wfType - TypeUnion)++[2]) | (TypeIsect <<= (wfType - TypeIsect)++[2]) - - // Types are no longer sequences. | (Type <<= wfType) ; // clang-format on - // Remove TypeArgs, New, Ident, Symbol, DoubleColon. - // Add RefVar, RefLet, Selector, FQFunction. - inline const auto wfExprReference = - (wfExprStructure - (TypeArgs | New | Ident | Symbol | DoubleColon)) | - RefVar | RefLet | Selector | FQFunction; + // Remove New, Ident, Symbol, Self, DoubleColon, TypeArgs. Add FQFunction. + inline const auto wfExprTypeReference = + (wfExprLambda - (New | Ident | Symbol | Self | DoubleColon | TypeArgs)) | + FQFunction; // clang-format off - inline const auto wfPassReference = + inline const auto wfPassTypeReference = wfPassTypeFlat - | (RefLet <<= Ident) - | (RefVar <<= Ident) | (FQFunction <<= FQType * Selector) - | (Expr <<= wfExprReference++[1]) + | (Expr <<= wfExprTypeReference++[1]) ; - // clang-format on - - // Remove If, Else. Add Conditional, TypeTest, Cast. - inline const auto wfExprConditionals = - (wfExprReference - (If | Else)) | Conditional | TypeTest | Cast; + // Remove Use. Remove implicit marker on fields. // clang-format off - inline const auto wfPassConditionals = - wfPassReference - | (Conditional <<= (If >>= Expr) * Block * Block) - | (TypeTest <<= Expr * Type) - | (Cast <<= Expr * Type) - - // Remove implicit marker. - | (FieldLet <<= Ident * Type * wfDefault)[Ident] - | (FieldVar <<= Ident * Type * wfDefault)[Ident] - - | (Expr <<= wfExprConditionals++[1]) + inline const auto wfPassResetImplicit = + wfPassTypeReference + | (ClassBody <<= (Class | TypeAlias | FieldLet | FieldVar | Function)++) + | (Block <<= (Class | TypeAlias | Expr)++[1]) + | (FieldLet <<= Ident * Type)[Ident] + | (FieldVar <<= Ident * Type)[Ident] ; // clang-format on // Remove Dot. Add Call, NLRCheck. inline const auto wfExprReverseApp = - (wfExprConditionals - Dot) | Call | NLRCheck; + (wfExprTypeReference - Dot) | Call | NLRCheck; // clang-format off inline const auto wfPassReverseApp = - wfPassConditionals - - // Remove Use. - | (ClassBody <<= (Class | TypeAlias | FieldLet | FieldVar | Function)++) - | (Block <<= (Class | TypeAlias | Expr)++[1]) + wfPassResetImplicit | (Call <<= (Selector >>= (Selector | FQFunction)) * Args) | (Args <<= Expr++) - | (NLRCheck <<= wfImplicit * Call) + | (NLRCheck <<= Call) | (Expr <<= wfExprReverseApp++[1]) ; // clang-format on - // Remove Unit, DontCare, Ellipsis, Selector, FQFunction. + // Remove Unit, True, False, DontCare, Ellipsis, Selector, FQFunction. // Add RefVarLHS. inline const auto wfExprApplication = - (wfExprReverseApp - (Unit | DontCare | Ellipsis | Selector | FQFunction)) | + (wfExprReverseApp - + (Unit | True | False | DontCare | Ellipsis | Selector | FQFunction)) | RefVarLHS; // clang-format off @@ -274,63 +304,34 @@ namespace verona // clang-format on // Remove Assign, Let, TupleLHS. Add Bind. - inline const auto wfExprBind = + inline const auto wfExprAssignment = (wfExprAssign - (Assign | Let | TupleLHS)) | Bind; // clang-format off inline const auto wfPassAssignment = wfPassLocalVar | (Bind <<= Ident * Type * Expr)[Ident] - | (Expr <<= wfExprBind) - ; - // clang-format on - - // Remove Lambda. - inline const auto wfExprLambda = wfExprBind - Lambda; - inline const auto wfNLRDefault = Default >>= NLRCheck | DontCare; - - // clang-format off - inline const auto wfPassLambda = - wfPassAssignment - | (FieldLet <<= Ident * Type * wfNLRDefault)[Ident] - | (FieldVar <<= Ident * Type * wfNLRDefault)[Ident] - | (Param <<= Ident * Type * wfNLRDefault)[Ident] - | (Expr <<= wfExprLambda) + | (Expr <<= wfExprAssignment) ; // clang-format on // Add FieldRef. - inline const auto wfExprAutoFields = wfExprLambda | FieldRef; + inline const auto wfExprAutoFields = wfExprAssignment | FieldRef; // clang-format off inline const auto wfPassAutoFields = - wfPassLambda + wfPassAssignment | (FieldRef <<= RefLet * Ident) | (Expr <<= wfExprAutoFields) ; // clang-format on - // clang-format off - inline const auto wfPassAutoCreate = - wfPassAutoFields - | (FieldLet <<= Ident * Type)[Ident] - | (FieldVar <<= Ident * Type)[Ident] - ; - // clang-format on - - // clang-format off - inline const auto wfPassDefaultArgs = - wfPassAutoCreate - | (Param <<= Ident * Type)[Ident] - ; - // clang-format on - // Remove NLRCheck. inline const auto wfExprNLRCheck = wfExprAutoFields - NLRCheck; // clang-format off inline const auto wfPassNLRCheck = - wfPassDefaultArgs + wfPassAutoFields // Add Return. | (Block <<= (Class | TypeAlias | Expr | Return)++[1]) @@ -357,8 +358,8 @@ namespace verona ; // clang-format on - // Remove RefLet. Add Copy, Move, Drop. - inline const auto wfExprDrop = (wfExprANF - RefLet) | Copy | Move | Drop; + // Remove RefLet. Add Copy, Move. + inline const auto wfExprDrop = (wfExprANF - RefLet) | Copy | Move; // clang-format off inline const auto wfPassDrop = @@ -368,11 +369,12 @@ namespace verona | (Drop <<= Ident) | (Block <<= (Class | TypeAlias | Bind | Return | LLVM | Move | Drop)++[1]) - | (Return <<= Move) + | (Return <<= (Ref >>= Move)) | (Tuple <<= (TupleFlatten | Copy | Move)++[2]) | (TupleFlatten <<= Copy | Move) | (Args <<= (Copy | Move)++) - | (Conditional <<= (If >>= Copy | Move) * Block * Block) + | (Conditional <<= + (If >>= Copy | Move) * (True >>= Block) * (False >>= Block)) | (TypeTest <<= (Ref >>= (Copy | Move)) * Type) | (Cast <<= (Ref >>= (Copy | Move)) * Type) | (FieldRef <<= (Ref >>= (Copy | Move)) * Ident) diff --git a/std/builtin/bool.verona b/std/builtin/bool.verona index e6c74c52..4c0605cd 100644 --- a/std/builtin/bool.verona +++ b/std/builtin/bool.verona @@ -11,6 +11,18 @@ class Bool from.bool() } + make_true(): Bool + { + let a = :[i1 1]: + Dyn::from_bool(a) + } + + make_false(): Bool + { + let a = :[i1 0]: + Dyn::from_bool(a) + } + bool(self: Bool): Bool { self