// Elaborate an id expression. When the identifier refers // to an object of type T (a variable or parameter), the // type of the expression is T&. Otherwise, the type of the // expression is the type of the declaration. // // TODO: There may be some contexts in which an unresolved // identifier can be useful. Unfortunately, this means that // we have to push the handling of lookup errors up one // layer, unless we to precisely establish contexts where // such identifiers are allowed. Expr* Elaborator::elaborate(Id_expr* e) { Scope::Binding const* b = stack.lookup(e->symbol()); if (!b) { std::stringstream ss; ss << "no matching declaration for '" << *e->symbol() << '\''; throw Lookup_error(locs.get(e), ss.str()); } // Annotate the expression with its declaration. Decl* d = b->second; e->declaration(d); // If the referenced declaration is a variable of // type T, then the type is T&. Otherwise, it is just T. Type const* t = d->type(); if (defines_object(d)) t = t->ref(); e->type(t); return e; }