Example #1
0
// 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});
}
Example #2
0
// 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);
}
Example #3
0
// 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);
}
Example #4
0
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;
}
Example #5
0
 Template_decl(Decl_list const& p, Decl& d)
   : Decl(d.name()), parms(p), cons(nullptr), decl(&d)
 {
   lingo_assert(!d.context());
   d.context(*this);
 }