зеркало из https://github.com/microsoft/verona.git
Stable type names (#642)
* use typevar function * lift lambda classes to the block, not the classbody * use node to token comparison * WIP, need to lift lambdas earlier * change all dir::once passes to bottomup * pick up trieste performance improvements * move conditionals pass earlier * detect bad default args early * separate reference and typereference * move the lambda pass earlier * move autocreate and defaultargs earlier * fix defaultargs param order * remove symtab from Block * discard std::equal result as a pairwise check * capture param type before error is emitted
This commit is contained in:
Родитель
5b548e01a1
Коммит
5d6a8a72a2
|
@ -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
|
||||
|
|
28
src/btype.h
28
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<Token>& 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);
|
||||
|
|
171
src/lang.cc
171
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<size_t, size_t> 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;
|
||||
|
|
94
src/lang.h
94
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
|
||||
{
|
||||
|
|
144
src/lookup.cc
144
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<Token> 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<Token> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace verona
|
|||
{
|
||||
return (def < other.def) && (bindings < other.bindings);
|
||||
}
|
||||
|
||||
size_t sub(Node& node);
|
||||
};
|
||||
|
||||
using Lookups = std::set<Lookup>;
|
||||
|
|
21
src/parse.cc
21
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()); },
|
||||
|
|
|
@ -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]) >>
|
||||
|
|
|
@ -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); },
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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); },
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}),
|
||||
|
|
|
@ -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()))
|
||||
{
|
||||
|
|
|
@ -12,44 +12,13 @@ namespace verona
|
|||
Nodes deps;
|
||||
};
|
||||
|
||||
size_t type_substitution(NodeMap<Node>& 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<NodeMap<Pending>>();
|
||||
auto ready = std::make_shared<Nodes>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -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}))
|
||||
|
|
|
@ -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<std::vector<track>>();
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
|
|
|
@ -10,118 +10,100 @@ namespace verona
|
|||
auto freevars = std::make_shared<std::vector<std::set<Location>>>();
|
||||
|
||||
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({});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
|
|
|
@ -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(_)
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
}),
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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; }),
|
||||
}};
|
||||
}
|
||||
}
|
|
@ -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)); },
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Bounds>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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<std::pair<NodeSet, Lookup>> 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"); },
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
105
src/readme.md
105
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]
|
||||
}
|
||||
|
||||
```
|
||||
|
|
223
src/subtype.cc
223
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<Assume> 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<Btype, bool> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,14 @@ namespace verona
|
|||
{
|
||||
using namespace trieste;
|
||||
|
||||
bool subtype(Node sub, Node sup);
|
||||
struct Bound
|
||||
{
|
||||
std::vector<Btype> lower;
|
||||
std::vector<Btype> upper;
|
||||
};
|
||||
|
||||
using Bounds = std::map<Location, Bound>;
|
||||
|
||||
bool subtype(Btype sub, Btype sup);
|
||||
bool subtype(Btype sub, Btype sup, Bounds& bounds);
|
||||
}
|
||||
|
|
176
src/wf.h
176
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)
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче