// Given declarations d1 and d2, a declaration error occurs when: // // - d2 changes the meaning of the name declared by d1, or if not that, then // - d1 and d2 are both objects, or // - d1 and d2 are functions that cannot be overloaded, or // - d1 and d2 are types having different kinds. // // Note that d1 precedes d2 in lexical order. // // TODO: Would it make sense to poison the declaration so that it's not // analyzed in subsequent passes? We could essentially replace the existing // overload set with one containing a poisoned declaration. Any expression // that uses that name would have an invalid type. We could then use this // to list the places where the error affects use. void check_declarations(Context& cxt, Decl const& d1, Decl const& d2) { struct fn { Context& cxt; Decl const& d2; void operator()(Decl const& d) { lingo_unhandled(d); } void operator()(Object_decl const& d1) { return check_declarations(cxt, d1, cast_as(d1, d2)); } void operator()(Function_decl const& d1) { return check_declarations(cxt, d1, cast_as(d1, d2)); } void operator()(Type_decl const& d1) { return check_declarations(cxt, d1, cast_as(d1, d2)); } }; if (typeid(d1) != typeid(d2)) { // TODO: Get the source location right. error(cxt, "declaration changes the meaning of '{}'", d1.name()); note("'{}' previously declared as:", d1.name()); // TODO: Don't print the definition. It's not germaine to // the error. If we have source locations, I wonder if we // can just point at the line. note("{}", d1); throw Declaration_error(); } apply(d1, fn{cxt, d2}); }
// Add the declaration d to the given scope. void declare(Context& cxt, Scope& scope, Decl& decl) { if (Overload_set* ovl = scope.lookup(decl.name())) declare(cxt, *ovl, decl); else scope.bind(decl); }
// Add the declaration d to the given scope. // // Note that this does not currently check to see if the declaration is // valid i.e., that it does not conflict a previous declaration. This is // only checkable if all declarations are fully elaborated (i.e., typed). void declare(Context& cxt, Scope& scope, Decl& decl) { if (Overload_set* ovl = scope.lookup(decl.name())) ovl->push_back(decl); else scope.bind(decl); }
Value Evaluator::eval(Call_expr const* e) { // Evaluate the function expression. Value v = eval(e->target()); Function_decl const* f = v.get_function(); // Evaluate each argument in turn. Value_seq args; args.reserve(e->arguments().size()); for (Expr const* a : e->arguments()) args.push_back(eval(a)); // Build the new call frame by pushing bindings // from each parameter to the corresponding argument. // // FIXME: Since everything type-checked, these *must* // happen to magically line up. However, it would be // a good idea to verify. Store_sentinel frame(*this); for (std::size_t i = 0; i < args.size(); ++i) { Decl* p = f->parameters()[i]; Value& v = args[i]; stack.top().bind(p->name(), v); } // Evaluate the function definition. // // TODO: Check result in case we've thrown // an exception (for example). Value result; Control ctl = eval(f->body(), result); if (ctl != return_ctl) throw std::runtime_error("function evaluation failed"); return result; }
Template_decl(Decl_list const& p, Decl& d) : Decl(d.name()), parms(p), cons(nullptr), decl(&d) { lingo_assert(!d.context()); d.context(*this); }