void Pipeline_checker::check_progression(Stage_set const& branches) { for (auto b : branches) { // If at any point we branch to a node we've already visited, // there's a possibility a loop exists. if (b->visited) { std::stringstream ss; ss << "Pipeline progress goes backwards through potential loop. Broken path: "; for (auto stage : path) ss << *stage->decl()->name() << " | "; ss << *b->decl()->name(); throw Lookup_error({}, ss.str()); } // If we advance to a table, confirm that the table is // one with a higher number than our current highest. if (b->is_table()) if (b->table()->number() <= highest_table) { std::stringstream ss; ss << "Pipeline path goes backwards through prior numbered table. Broken path: "; for (auto stage : path) ss << *stage->decl()->name() << " | "; ss << *b->decl()->name(); throw Lookup_error({}, ss.str()); } } }
Type& Parser::on_type_name(Token tok) { Simple_id& id = build.get_id(tok); Decl& decl = simple_lookup(cxt, current_scope(), id); if (Type* type = get_type_for_decl(cxt, decl)) return *type; throw Lookup_error("'{}' does not name a type", id); }
// FIXME: What if the identifier refers to a set of declarations? Decl& Parser::on_concept_name(Token tok) { Simple_id& id = build.get_id(tok); Decl& decl = simple_lookup(cxt, current_scope(), id); if (is<Concept_decl>(&decl)) return decl; throw Lookup_error("'{}' does not name a concept", id); }
// Check if the template-id n refers to a type. Type& Parser::on_type_name(Name& n) { Template_id& id = cast<Template_id>(n); Template_decl& tmp = id.declaration(); Term_list& args = id.arguments(); Decl& decl = specialize_template(cxt, tmp, args); if (Type* type = get_type_for_decl(cxt, decl)) return *type; throw Lookup_error("not a type name"); }
Type const* Elaborator::elaborate(Id_type const* t) { Scope::Binding const* b = stack.lookup(t->symbol()); if (!b) { std::stringstream ss; ss << "no matching declaration for '" << *t->symbol() << '\''; throw Lookup_error(locs.get(t), ss.str()); } // Determine if the name is a type declaration. Decl* d = b->second; if (Record_decl* r = as<Record_decl>(d)) { return get_record_type(r); } else { std::stringstream ss; ss << '\'' << *t->symbol() << "' does not name a type"; throw Lookup_error(locs.get(t), ss.str()); } }
Type& Parser::on_type_alias(Token tok) { Simple_id& id = build.get_id(tok); Decl& decl = simple_lookup(cxt, current_scope(), id); if (Type_parm* d = as<Type_parm>(&decl)) return build.get_typename_type(*d); // TODO: Actually support type aliases. throw Lookup_error("'{}' does not name a type", id); }
// Perform qualified lookup to resolve the declaration referred to by obj.n. // // FIXME: This needs to perform class lookup, not qualified lookup. static Expr& make_member_ref(Context& cxt, Expr& obj, Simple_id& name) { Type& type = obj.type(); Decl_list decls = qualified_lookup(cxt, type, name); if (decls.empty()) { error(cxt, "no matching declaration for '{}'", name); throw Lookup_error(); } if (decls.size() == 1) return make_resolved_mem_ref(cxt, obj, decls.front()); else return make_mem_overload_ref(cxt, obj, name, std::move(decls)); }
// Create a declarative binding for d. This also checks // that the we are not redefining a symbol in the current // scope. void Scope_stack::declare(Decl* d) { Scope& scope = current(); // TODO: If we allow overloading, then this is // where we would handle that. if (scope.lookup(d->name())) { // TODO: Add a note that points to the previous // definition. std::stringstream ss; ss << "redefinition of '" << *d->name() << "'\n"; throw Lookup_error({}, ss.str()); } // Create the binding. scope.bind(d->name(), d); // Set d's declaration context. d->cxt_ = context(); }
// 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; }
void Pipeline_checker::check_stage(Decl const* d, Sym_set const& reqs) { bool error = false; std::stringstream ss; for (auto field : reqs) { auto search = stack.lookup(field); if (!search) { error = true; ss << "Field " << *field << " required but not decoded.\n"; } } if (error) { ss << "Broken path: "; for (auto stage : path) { ss << *stage->decl()->name() << " | "; } throw Lookup_error({}, ss.str()); } }
Type& Parser::on_enum_name(Name&) { throw Lookup_error("not an enum"); }
Decl& Parser::on_namespace_alias(Name&) { throw Lookup_error("not a namespace alias"); }
Decl& Parser::on_namespace_name(Name&) { throw Lookup_error("not a namespace"); }
Decl& Parser::on_namespace_name(Token id) { throw Lookup_error("not a namespace"); }
Type& Parser::on_type_alias(Name&) { throw Lookup_error("not a type alias"); }
Type& Parser::on_union_name(Name&) { throw Lookup_error("not a union"); }
Type& Parser::on_class_name(Name&) { throw Lookup_error("not a class"); }