// -*- mode: java; c-basic-offset: 4 -*-
//
// Copyright 2001-2002 by the Regents of the University of California.
// All rights reserved.
//
//      HARMONIA: C language module name resolution
//
// RCS Info: $Revision: 1.1 $ on $Date: 2003/05/12 01:49:32 $
//           $Source: /home/cs/harmonia/cvsroot/www/harmonia/projects/langs/c/c-resolve.def,v $
//


// Not usable yet, alas:
// -*- mode: harmonia-astdef2; c-basic-offset: 4 -*-
import "Node.def";
import "ParentNode.def";
import "ListNode.def";

import "AmbigNode.def";
import "GLRStateMixin.def";

include "common/stl.h"
include "common/PooledString.h"
include "lk/isemant/Scope.h"
include "lk/isemant/NestedScope.h"
include "lk/isemant/OverlayScope.h"
include "lk/isemant/SemantErrors.h"
include "lk/node/UltraRoot.h"

include "c.h"

phylum trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
    
    public virtual method void semant() {}
}

// trans_unit -> SeqR0E0:ext_decls 
operator TranslationUnit extends ParentNode, trans_unit, GLRStateMixin {
    public slot TypecheckContext *type_context;

    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	for (ext_decls_iterator i = get_ext_decls(); i; ++i) {
	    if (!i.get_ext_decl()->resolve_idents(ctx, idents))
		return false;
	}
	return true;
    }

    public virtual method void semant() {
	clear_semant_errors_subtree(cast_to_Node());
 	for (ext_decls_iterator i = get_ext_decls(); i; ++i) {
	    bool keep_going = root()->get_bool_prop(ALWAYS_DO_SEMANTICS);
	    //fprintf(stderr, "keep_going is %d\n", keep_going);
	    bool is_preproc
                = i.get_ext_decl()->is_preproc_directive(keep_going);
 	    if (is_preproc)
 		return;
	}
	{
	    ResolveContext ctx;
	    RootIdentTable filescope_idents;
	    if (!resolve_idents(&ctx, &filescope_idents))
		error("Unexpected mismatch at top level!\n");
	} {
	    type_context = new TypecheckContext();
	    check_types(type_context);
	}
    }
}

operator no_trans_unit extends Node, trans_unit, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
    public virtual method void semant() {}
}

operator trans_unit_sym extends AmbigNode, trans_unit, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in translation unit!\n");
	return false;
    }
    public virtual method void semant() {
	error("Unexpected ambiguity in translation unit!\n");
    }
}

phylum ext_decl depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
    public virtual method bool is_preproc_directive(bool keep_going) {
	return false;
    }
}

// ext_decl -> SeqR1E0:specs declr:declr SeqR1E2:decls comp_stmt:body 
operator FunctionDefinition extends ParentNode, ext_decl, GLRStateMixin
depends on decl, declr, Declaration {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	decl::ResolveType base_type;
	Localizer<DeclContext> save(&ctx->decl_cx);
	Localizer<LabelTable *> save2(&ctx->labels); // not really needed
	ctx->decl_cx = FUNC_DEF;
	for (specs_iterator i = get_specs(); i; ++i) {
	    if (!i.get_spec()->resolve_idents(ctx, idents))
		return false;
	    i.get_spec()->get_resolve_type(ctx, &base_type);
	}
	if (!Declaration::check_resolve_type(ctx, &base_type, this))
	    return false;
	if (!get_declr()->resolve_idents(ctx, idents, &base_type))
	    return false;
	ctx->decl_cx = FUNC_DEF;
	declr::FunctionDeclType type = get_declr()->declares_func();
	if (type == declr::NOT_A_FUNCTION) {
	    ambig_semant_error(ctx, this, "function declaration "
			       "does not declare a function");
	    if (ctx->inside_ambig)
		return false;
	}
	NestedIdentTable func_idents;
	func_idents.add_parent(idents);
	if (type == declr::ANSI_FUNCTION) {
	    if (get_decls())
		set_semant_error(this, "ANSI functions cannot take extra "
				 "declarations");
	    get_declr()->declare_ansi_idents(ctx, &func_idents);
	} else if (type == declr::K_AND_R_FUNCTION) {
	    ctx->decl_cx = KnR_PARAM;
	    for (decls_iterator i = get_decls(); i; ++i) {
		if (!i.get_decl()->resolve_idents(ctx, &func_idents))
		    return false;
	    }
	    get_declr()->check_KnR_idents(ctx, &func_idents);
	}
	ctx->decl_cx = NO_DECL;
	ctx->labels = new LabelTable();
	int ret = get_body()->resolve_idents(ctx, &func_idents, false);
	for (LabelTable::iterator i 
		 = ctx->labels->get_bindings(); i; ++i) {
	    LabelInfo *info = i.get_entity();
	    if (info->defs.size() == 0) {
		for (vector<GotoStatement *, gc_allocator>::iterator i
			 = info->uses.begin(); i != info->uses.end(); ++i) {
		    set_semant_error(*i, "Undefined label %s",
				     info->name.chars());
		}
	    } else if (info->defs.size() > 1) {
		for (vector<LabeledStatement *, gc_allocator>::iterator i 
			 = info->defs.begin(); i != info->defs.end(); ++i) {
		    set_semant_error(*i, "Duplicate definition of label %s",
				     info->name.chars());
		}
	    } else {
		for (vector<GotoStatement *, gc_allocator>::iterator i
			 = info->uses.begin(); i != info->uses.end(); ++i) {
		    (*i)->target.set_value(vg(), info->defs[0]);
		    (*i)->compute_synth_attrs(true);
		}
	    }
	}
	ctx->labels = 0;
	return ret;
    }
}

// ext_decl -> decl:decl 
operator OtherExtDeclaration extends ParentNode, ext_decl, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	Localizer<DeclContext> save(&ctx->decl_cx);
	ctx->decl_cx = FILE_SCOPE;
	bool res = get_decl()->resolve_idents(ctx, idents);
// 	cerr << "Ident table is now:" << endl;
// 	idents->dump(cerr);
	return res;
    }
}

operator PreprocessorJunk {
    public virtual method bool is_preproc_directive(bool keep_going) {
	if (!keep_going) {
	    set_semant_error(this, "Can't run semantics with preprocessor "
	                     "directives");
	    return true;
	}
	return false;
    }
}

operator no_ext_decl extends Node, ext_decl, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

operator ext_decl_sym extends AmbigNode, ext_decl, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return resolve_idents_func<ext_decl, ext_decl_sym>(ctx, idents, this);
    }
}

phylum decl_spec depends on decl {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	error("Unexpected call to decl_spec::get_resolve_type\n");
    }
}

// decl_spec -> stor_class_spec:spec 
operator StorageDeclSpec extends ParentNode, decl_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	get_spec()->get_resolve_type(ctx, type);
    }
}

// decl_spec -> type_spec:spec 
operator TypeSpecDeclSpec extends ParentNode, decl_spec, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_spec()->resolve_idents(ctx, idents);
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	get_spec()->get_resolve_type(ctx, type);
    }
}

// decl_spec -> type_qual:qual 
operator TypeQualDeclSpec extends ParentNode, decl_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	get_qual()->get_resolve_type(ctx, type);
    }
}

// decl_spec -> INLINE:qual 
operator FunctionQualDeclSpec extends ParentNode, decl_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	/* nothing to do */
    }
}

operator decl_spec_sym extends AmbigNode, decl_spec, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in declaration specifier!\n");
	return true;
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	error("Unexpected ambiguity in declaration specifier!\n");
    }
}

phylum declr depends on decl {
    public enum FunctionDeclType {
	NOT_A_FUNCTION,
	K_AND_R_FUNCTION,
	ANSI_FUNCTION
    };

    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      decl::ResolveType *base_type) {
	return true;
    }
    public virtual method declr::FunctionDeclType declares_func() {
	error("Not reached\n");
	return NOT_A_FUNCTION;
    }
    public virtual method IDSYM* find_name() { 
	return 0;
    }
    public virtual method void declare_ansi_idents(ResolveContext *ctx,
						   IdentTable *func_idents) {
	error("Unexpected call to declr::declare_ansi_idents\n");
    }
    public virtual method void declare_ident(ResolveContext *ctx,
					     IdentTable *func_idents) {
	error("Unexpected call to declr::declare_ident\n");
    }
    public virtual method void check_KnR_idents(ResolveContext *ctx,
						IdentTable *func_idents) {
	error("Unexpected call to declr::check_KnR-idents\n");
    }
}

// declr -> OptR47E0:ptr direct_declr:direct 
operator Declarator extends ParentNode, declr, GLRStateMixin {
    public versioned slot Vptr identifier = NULL;
    versioned Semantics attribute identifier
	= TypedVal((Node*)identifier.value(vg(), gvid));

    public versioned slot Vptr linkage_link = NULL;
    versioned Semantics attribute linkage_link
	= TypedVal((Node*)linkage_link.value(vg(), gvid));

    public slot int decl_context;

    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      decl::ResolveType *base_type) {
	// decl TypeInfo my_type = *base_type;
	// if (has_ptr())
	//     get_ptr()->specify(my_type)
	if (!get_direct()->resolve_idents(ctx, idents))
	    return false;
	IDSYM *name_node = find_name();
	identifier.set_value(vg(), name_node->cast_to_Node());
	compute_synth_attrs(true);
	PooledString id_name = name_node->pooled_string();
	IdentInfo::IdentType id_type;
	if (base_type->storage == decl::TYPEDEF_STORAGE) {
	    id_type = IdentInfo::TYPEDEF_ID;
	    name_node->set_subspecies(ctx, IDSYM::TYPEDEF_DECL_NAME);
	} else {
	    id_type = IdentInfo::OBJECT_ID;
	    name_node->set_subspecies(ctx, IDSYM::DECLARATION_NAME);
	}
	IdentInfo *info = new IdentInfo(id_name, id_type, this);
	decl_context = ctx->decl_cx;
	switch (ctx->decl_cx) {
	case FOR_INIT:
	case BLOCK:
	    if (base_type->storage != decl::EXTERN_STORAGE) {
		if (idents->has_ident_binding(id_name)) {
		    set_semant_error(this, "Identifier `%s' redefined",
				     id_name.chars());
		    return true;
		}
		break;
	    }
	    /* fall through */
	case FILE_SCOPE:
	case FUNC_DEF:
	    {
		IdentInfo *prev_info = idents->resolve_ident(id_name);
		info->link = prev_info;
		if (prev_info) {
		    linkage_link.set_value(vg(), prev_info->definition);
		    compute_synth_attrs(true);
		}
		info->defined = (ctx->decl_init || ctx->decl_cx == FUNC_DEF);
		if (prev_info && prev_info->type != IdentInfo::OBJECT_ID)
		    set_semant_error(this, "Identifier `%s' redeclared as a "
				     "different kind of identifier",
				     id_name.chars());
		if (prev_info && prev_info->defined && info->defined)
		    set_semant_error(this, "Duplicate definition of `%s'",
				     id_name.chars());
		if (base_type->storage == decl::STATIC_STORAGE) {
		    if (prev_info &&
			prev_info->linkage != IdentInfo::INTERNAL_LINKAGE) {
			set_semant_error(this, "Identifier `%s' was declared "
					 "non-static and then static",
					 id_name.chars());
		    }
		    info->linkage = IdentInfo::INTERNAL_LINKAGE;
		} else if (base_type->storage == decl::EXTERN_STORAGE ||
			   (ctx->decl_cx == FUNC_DEF &&
			    base_type->storage == decl::NO_STORAGE)) {
		    if (prev_info &&
			prev_info->linkage != IdentInfo::NO_LINKAGE) {
			info->linkage = prev_info->linkage;
		    } else {
			info->linkage = IdentInfo::EXTERNAL_LINKAGE;
		    }
		} else if (base_type->storage == decl::NO_STORAGE) {
		    if (!prev_info) {
			info->linkage = IdentInfo::EXTERNAL_LINKAGE;
		    } else if (prev_info->linkage
			       == IdentInfo::INTERNAL_LINKAGE) {
			set_semant_error(this, "Identifier `%s' was declared "
					 "static and then non-static",
					 id_name.chars());
		    } else {
			info->linkage = prev_info->linkage;
		    }
		} else if (base_type->storage == decl::TYPEDEF_STORAGE) {
		    if (idents->has_ident_binding(id_name)) {
			set_semant_error(this, "Identifier `%s' redefined",
					 id_name.chars());
			return true;
		    }		    
		} else {
		    error("Unexpected storage class in declaration\n");
		}
		break;
	    }

	case KnR_PARAM:
	    name_node->set_subspecies(ctx, IDSYM::KNR_PARAM_DECL_NAME);
	    goto parameter;
	case DEF_PARAM:
	case DECL_PARAM:
	    name_node->set_subspecies(ctx, IDSYM::PARAM_DECL_NAME);
	parameter:
	    if (idents->has_ident_binding(id_name)) {
		set_semant_error(this, "Duplicate parameter `%s'",
				 id_name.chars());
		return true;
	    }
	    break;

	case STRUCT_UNION_CX:
	    assert(ctx->aggr_members);
	    name_node->set_subspecies(ctx, IDSYM::AGGR_MEMBER_DECL_NAME);
	    if (ctx->aggr_members->has_binding(id_name)) {
		set_semant_error(this, "Duplicate member `%s'",
				 id_name.chars());
	    } else {
		info->type = IdentInfo::STRUCT_UNION_MEMBER;
		ctx->aggr_members->add(id_name, info);
	    }
	    return true;

	case NO_DECL:
	default:
	    error("Strange decl context\n");
	    break;
	}
	idents->add_ident(id_name, info);
	return true;
    }
    public virtual method void declare_ident(ResolveContext *ctx,
					     IdentTable
					     *func_idents) {
	PooledString id_name = find_name()->pooled_string();
	IdentInfo *info = new IdentInfo(id_name, IdentInfo::OBJECT_ID, this);
	func_idents->add_ident(id_name, info);
    }
    public virtual method declr::FunctionDeclType declares_func() {
	return get_direct()->declares_func();
    }
    public virtual method IDSYM* find_name() {
	return get_direct()->find_name();
    }
    public virtual method void declare_ansi_idents(ResolveContext *ctx,
						   IdentTable *func_idents) {
	get_direct()->declare_ansi_idents(ctx, func_idents);
    }
    public virtual method void check_KnR_idents(ResolveContext *ctx,
						IdentTable *func_idents) {
	get_direct()->check_KnR_idents(ctx, func_idents);
    }
}

operator declr_sym extends AmbigNode, declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      decl::ResolveType *base_type) {
	error("Unexpected ambiguity in declarator!\n");
	return false;
    }
    public virtual method IDSYM* find_name() { 
	error("Unexpected ambiguity in declarator!\n");
	return 0;
    }
    public virtual method declr::FunctionDeclType declares_func() {
	error("Unexpected ambiguity in declarator!\n");
	return NOT_A_FUNCTION;
    }
    public virtual method void declare_ansi_idents(ResolveContext *ctx,
						   IdentTable *func_idents) {
	error("Unexpected ambiguity in declarator!\n");
    }
    public virtual method void declare_ident(ResolveContext *ctx,
					     IdentTable
					     *func_idents) {
	error("Unexpected ambiguity in declarator!\n");
    }
    public virtual method void check_KnR_idents(ResolveContext *ctx,
						IdentTable *func_idents) {
	error("Unexpected call to declr::check_KnR-idents\n");
    }
}

phylum direct_declr depends on trans_unit, declr {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {

	error("Unexpected call too direct_declr::resolve_idents\n");
	return true;
    }
    public virtual method IDSYM* find_name() { 
	return 0;
    }
    public virtual method declr::FunctionDeclType declares_func() {
	return declr::NOT_A_FUNCTION;
    }
    public virtual method void declare_ansi_idents(ResolveContext *ctx,
						   IdentTable *func_idents) {
	error("Unexpected call to direct_declr::declare_ansi_idents\n");
    }
    public virtual method void check_KnR_idents(ResolveContext *ctx,
						IdentTable *func_idents) {
	error("Unexpected call to direct_declr::check_KnR_idents\n");
    }
    public virtual method bool could_be_typedef(ResolveContext *ctx,
						IdentTable *idents) {
	return false;
    }
}

// direct_declr -> IDSYM:name 
operator DeclarNameDirDeclr extends ParentNode, direct_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {

	return true;	    
    }
    public virtual method IDSYM* find_name() { 
	return get_name();
    }
    public virtual method bool could_be_typedef(ResolveContext *ctx,
						IdentTable *idents) {
	IdentInfo *info = idents->resolve_ident(get_name()->pooled_string());
	if (info && info->type == IdentInfo::TYPEDEF_ID)
	    return true;
	return false;
    }
}

// direct_declr -> '(' ptr:(ptr:pointer)? direct:direct_declr ')'
operator ParenNameDirDeclr extends ParentNode, direct_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if ((ctx->decl_cx == DEF_PARAM || ctx->decl_cx == DECL_PARAM) &&
	    get_direct()->could_be_typedef(ctx, idents) && ctx->inside_ambig) {
	    ambig_semant_error(ctx,  this, "single parenthesized typename in "
			       "parameter must be abstract function "
			       "declaration");
	    return false;
	}
	return get_direct()->resolve_idents(ctx, idents);
    }
    public virtual method IDSYM* find_name() { 
	return get_direct()->find_name();
    }
    public virtual method declr::FunctionDeclType declares_func() {
	return get_direct()->declares_func();
    }
}

// direct_declr -> direct_declr:declr '[' SeqR52E2:quals OptR52E3 ']' 
operator BasicArrayDirDeclr extends ParentNode, direct_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_declr()->resolve_idents(ctx, idents))
	    return false;
	if (has_expr())
	    return get_expr_expr()->resolve_idents(ctx, idents);
	else
	    return true;
    }
    public virtual method IDSYM* find_name() { 
	return get_declr()->find_name();
    }
}

// direct_declr -> direct_declr:declr '[' SeqR52E2:quals1 STATIC SeqR52E2:quals2 assign_expr:expr ']' 
operator StaticArrayDirDeclr extends ParentNode, direct_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_declr()->resolve_idents(ctx, idents))
	    return false;
	return get_expr()->resolve_idents(ctx, idents);
    }
    public virtual method IDSYM* find_name() { 
	return get_declr()->find_name();
    }
}

// direct_declr -> direct_declr:declr '[' SeqR52E2:quals '*' ']' 
operator VariableArrayDirDeclr 
extends ParentNode, direct_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {

	return get_declr()->resolve_idents(ctx, idents);
    }
    public virtual method IDSYM* find_name() { 
	return get_declr()->find_name();
    }
}

// direct_declr -> direct_declr:declr '(' param_type_list:params ')' 
operator TypeFuncListDirDeclr extends ParentNode, direct_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_declr()->resolve_idents(ctx, idents))
	    return false;
	Localizer<DeclContext> save(&ctx->decl_cx);
	if (ctx->decl_cx == FUNC_DEF)
	    ctx->decl_cx = DEF_PARAM;
	else
	    ctx->decl_cx = DECL_PARAM;
	NestedIdentTable param_table;
	param_table.add_parent(idents);
	return get_params()->resolve_idents(ctx, &param_table);
    }
    public virtual method IDSYM* find_name() { 
	return get_declr()->find_name();
    }
    public virtual method declr::FunctionDeclType declares_func() {
	return declr::ANSI_FUNCTION;
    }
    public virtual method void declare_ansi_idents(ResolveContext *ctx,
						   IdentTable *func_idents) {
	get_params()->declare_ansi_idents(ctx, func_idents);
    }
}

// direct_declr -> direct_declr:declr '(' SeqR56E2:idents ')' 
operator IDFuncListDirDeclr extends ParentNode, direct_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_declr()->resolve_idents(ctx, idents))
	    return false;
	NestedIdentTable param_ids;
	param_ids.add_parent(idents);
	for (idents_iterator i = get_idents(); i; ++i) {	    
	    IDSYM *token = i.get_ident();
	    PooledString id_name = token->pooled_string();
	    IdentInfo *info = new IdentInfo(id_name, IdentInfo::OBJECT_ID, 
					    this);
	    IdentInfo *old_info = idents->resolve_ident(id_name);
	    if (old_info && old_info->type == IdentInfo::TYPEDEF_ID) {
		ambig_semant_error(ctx, this, "typedef name can't be "
				   "redeclared as an old-style function "
				   "parameter");
		if (ctx->inside_ambig)
		    return false;
	    }
	    if (param_ids.has_ident_binding(id_name)) {
		// It would be nice to put this on "token" rather than "this",
		// but tokens are shared between alternatives, so the error
		// message would leak out to the other alternative.
		// We could fix this by postponing setting error messages,
		// but that would be a pain.
		set_semant_error(this, "Parameter `%s' redefined",
				 id_name.chars());
	    } else {
		param_ids.add_ident(id_name, info);
	    }
	}
	return true;
    }
    public virtual method void check_KnR_idents(ResolveContext *ctx,
						IdentTable *func_idents) {
	int num_params = 0;
	for (idents_iterator i = get_idents(); i; ++i) {
	    IDSYM *token = i.get_ident();
	    PooledString id_name = token->pooled_string();
	    token->set_subspecies(ctx, IDSYM::KNR_PARAM_LIST_NAME);
	    if (!func_idents->has_ident_binding(id_name)) {
		set_semant_error(token, "Parameter %s was not declared",
				 id_name.chars());		
	    }
	    num_params++;
	}
	if (num_params < func_idents->idents_size()) {
	    int extra = func_idents->idents_size() - num_params;
	    set_semant_error(this, "%d variable%s declared but "
			     "not present in prototype", extra,
			     (extra != 1 ? "s" : ""));
	}
    }
    public virtual method IDSYM* find_name() { 
	return get_declr()->find_name();
    }
    public virtual method declr::FunctionDeclType declares_func() {
	return declr::K_AND_R_FUNCTION;
    }
}

operator direct_declr_sym extends AmbigNode, direct_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return resolve_idents_func<direct_declr, direct_declr_sym>
	    (ctx, idents, this);
    }
    public virtual method IDSYM* find_name() {
	return direct_declr::phylum_cast(primary_alternative())->find_name();
    }
    public virtual method declr::FunctionDeclType declares_func() {
	return direct_declr::phylum_cast(primary_alternative())
	    ->declares_func();
    }
    public virtual method void check_KnR_idents(ResolveContext *ctx,
						IdentTable *func_idents) {
	return direct_declr::phylum_cast(primary_alternative())
	    ->check_KnR_idents(ctx, func_idents);
    }
}

phylum decl depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }

    public enum StorageClass {
	NO_STORAGE,
	TYPEDEF_STORAGE,
	STATIC_STORAGE,
	EXTERN_STORAGE,
	AUTO_STORAGE,
	REGISTER_STORAGE
    };

    public struct ResolveType : public Collectable {
	bool declares_typedef : 1;
	bool uses_typename : 1;
	bool uses_nontypename : 1;
	bool multiple_typename : 1;
	StorageClass storage;

	ResolveType() : declares_typedef(false), uses_typename(false),
	    uses_nontypename(false), multiple_typename(false),
	    storage(NO_STORAGE) {}
    };
}

// decl -> SeqR1E0:specs SeqR8E1:declrs ';' 
operator Declaration extends ParentNode, decl, GLRStateMixin {
    public slot StorageClass storage_class;

    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	ResolveType base_type;
	for (specs_iterator i = get_specs(); i; ++i) {
	    if (!i.get_spec()->resolve_idents(ctx, idents))
		return false;
	    i.get_spec()->get_resolve_type(ctx, &base_type);
	}	
	if (!check_resolve_type(ctx, &base_type, this))
	    return false;
	storage_class = base_type.storage;
	for (declrs_iterator i = get_declrs(); i; ++i) {
	    if (!i.get_declr()->resolve_idents(ctx, idents, &base_type))
		return false;
	}
	return true;
    }

    public static method bool check_resolve_type(ResolveContext *ctx,
						 ResolveType *type,
						 Node *node) {
	if (type->multiple_typename) {
	    ambig_semant_error(ctx, node, "too many typedef "
			       "names in declaration");
	    if (ctx->inside_ambig)
		return false;
	} else if (type->uses_typename && type->uses_nontypename) {
	    ambig_semant_error(ctx, node,
			       "specifiers include both a typedef name and "
			       "another type specifier");
	    if (ctx->inside_ambig)
		return false;
	} else if (!type->uses_typename && !type->uses_nontypename) {
	    ambig_semant_error(ctx, node, "C99 forbids a declaration with "
			       "no type");
	    if (ctx->inside_ambig)
		return false;
	}
	return true;
    }
}

operator no_decl extends Node, decl, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

operator decl_sym extends AmbigNode, decl, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return resolve_idents_func<decl, decl_sym>(ctx, idents, this);
    }
}

phylum init_declr depends on decl {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      decl::ResolveType *base_type) {
	return true;
    }
}

// init_declr -> declr:declr OptR39E1:init 
operator InitDeclr extends ParentNode, init_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      decl::ResolveType *base_type) {
	Localizer<bool> save(&ctx->decl_init);
	ctx->decl_init = has_init();
	if (!get_declr()->resolve_idents(ctx, idents, base_type))
	    return false;
	if (has_init()) {
	    return get_init_init()->resolve_idents(ctx, idents);
	}
	return true;
    }
}

operator init_declr_sym extends AmbigNode, init_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      decl::ResolveType *base_type) {
	error("Unexpected ambiguity in declrator!\n");
	return true;
    }
}


phylum comp_stmt depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      bool new_scope = true) {
	return true;
    }
}

// comp_stmt -> '{' SeqR88E1:items '}' 
operator BlockStatement extends ParentNode, comp_stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      bool new_scope = true) {
	NestedIdentTable local_idents;
	IdentTable *table;
	if (new_scope) {
	    local_idents.add_parent(idents);
	    table = &local_idents;
	} else {
	    table = idents;
	}
	for (items_iterator i = get_items(); i; ++i) {
	    if (!i.get_item_()->resolve_idents(ctx, table))
		return false;
	}
	return true;	    
    }
}

operator comp_stmt_sym extends AmbigNode, comp_stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      bool new_scope) {
	error("Unexpected ambiguity in block!\n");
	return true;
    }
}

phylum stor_class_spec depends on decl {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	error("Unexpected call to stor_class_spec::get_resolve_type\n");
    }
}

// stor_class_spec -> AUTO 
operator AutoStrClassSpec extends ParentNode, stor_class_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	if (type->storage != decl::NO_STORAGE)
	    set_semant_error(this, "Duplicate storage class specifier");
	if (ctx->decl_cx != BLOCK &&
	    ctx->decl_cx != FOR_INIT)
	    set_semant_error(this, "'auto' is not allowed in this context");
	type->storage = decl::AUTO_STORAGE;
    }
}

// stor_class_spec -> REGISTER 
operator RegisterStrClassSpec
extends ParentNode, stor_class_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	if (type->storage != decl::NO_STORAGE)
	    set_semant_error(this, "Duplicate storage class specifier");
	if (ctx->decl_cx == FILE_SCOPE ||
	    ctx->decl_cx == FUNC_DEF)
	    set_semant_error(this, "File-scope identifiers may not "
			     "be declared 'register'");
	type->storage = decl::REGISTER_STORAGE;
    }
}

// stor_class_spec -> STATIC 
operator StaticStrClassSpec
extends ParentNode, stor_class_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	if (type->storage != decl::NO_STORAGE)
	    set_semant_error(this, "Duplicate storage class specifier");
	if (ctx->decl_cx != FILE_SCOPE &&
	    ctx->decl_cx != BLOCK &&
	    ctx->decl_cx != FUNC_DEF)
	    set_semant_error(this, "'static' is not allowed in this context");
	type->storage = decl::STATIC_STORAGE;
    }
}

// stor_class_spec -> EXTERN 
operator ExternStrClassSpec
extends ParentNode, stor_class_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	if (type->storage != decl::NO_STORAGE)
	    set_semant_error(this, "Duplicate storage class specifier");
	if (ctx->decl_cx == KnR_PARAM ||
	    ctx->decl_cx == DEF_PARAM ||
	    ctx->decl_cx == DECL_PARAM)
	    set_semant_error(this, "Parameters can't be 'extern'");
	type->storage = decl::EXTERN_STORAGE;
    }
}

// stor_class_spec -> TYPEDEF
operator TypedefStrClassSpec
extends ParentNode, stor_class_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	if (type->storage != decl::NO_STORAGE)
	    set_semant_error(this, "Duplicate storage class specifier");
	if (ctx->decl_cx != BLOCK &&
	    ctx->decl_cx != FILE_SCOPE)
	    set_semant_error(this, "You can't make a typedef out of that");
	type->storage = decl::TYPEDEF_STORAGE;
    }
}

operator no_stor_class_spec extends Node, stor_class_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	/* nothing */
    }
}

operator stor_class_spec_sym
extends AmbigNode, stor_class_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	error("Unexpected ambiguity in storage class specifier\n");
    }
}

phylum type_spec depends on decl {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	error("Unexpected call to type_spec::get_resolve_type\n");
    }
}

// type_spec -> VOID 
operator VoidTypeSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> CHAR 
operator CharTypeSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> SHORT 
operator ShortTypeSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> INT 
operator IntTypeSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> LONG 
operator LongTypeSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> FLOAT 
operator FloatTypeSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> DOUBLE 
operator DoubleTypeSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> SIGNED 
operator SignedTypeSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> UNSIGNED 
operator UnsignedTypeSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> _BOOL 
operator BooleanTypeSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> _COMPLEX 
operator ComplexTypeSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> _IMAGINARY 
operator ImaginaryTypeSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> STRUCT OptR29E1:name '{' SeqR29E3:decls '}' 
operator StructDefinitionSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (has_name()) {
	    get_name_name()->set_subspecies(ctx, IDSYM::STRUCT_TAG_NAME);
	    PooledString tag = get_name_name()->pooled_string();
	    IdentInfo *info;
	    if (idents->has_tag_binding(tag)) {
		info = idents->resolve_tag(tag);
		assert(info);
		if (info->type != IdentInfo::STRUCT_TAG)
		    set_semant_error(this,"`%s' is a %s tag, not a struct tag",
				     tag.chars(),
				     info->type == IdentInfo::UNION_TAG ?
				     "union" : "enum");
		/* There's a subtle bug here with the interaction of this
		   check with ambiguity. If there's a forward declaration of
		   the struct before the current ambiguous region, as well
		   as this definition inside, we might have already seen this
		   definition before, and made a note that it's already been
		   defined. However, because we only localize changes to the
		   pointers in the symbol table, changes to members of the
		   info struct 'leak out'. The second condition here is a
		   quick check to avoid giving an error in that case. If
		   ambiguities covered larger portions of the syntax tree,
		   we'd need a better fix of actually saving and possibly
		   restoring all these fields. */
		else if (info->defined && info->definition != this)
		    set_semant_error(this, "Duplicate definition of"
				     " struct `%s'",
				     tag.chars());
		else if (info->definition && !info->defined) {
		    StructReferenceSpec *ref
		        = (StructReferenceSpec *)info->definition;
		    ref->completion.set_value(vg(), this);
		    ref->compute_synth_attrs(true);
		}
	    } else {
		info = new IdentInfo(tag, IdentInfo::STRUCT_TAG,
						this);
		idents->add_tag(tag, info);
	    }
	    info->definition = this;
	    info->defined = true;
	}
	Localizer<DeclContext> save(&ctx->decl_cx);
	ctx->decl_cx = STRUCT_UNION_CX;
	Localizer<SingleIdentTable *> save2(&ctx->aggr_members);
	ctx->aggr_members = new SingleIdentTable();
	for (decls_iterator i = get_decls(); i; ++i) {
	    if (!i.get_decl()->resolve_idents(ctx, idents))
		return false;
	}
	return true;
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> UNION OptR29E1:name '{' SeqR29E3:decls '}' 
operator UnionDefinitionSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (has_name()) {
	    get_name_name()->set_subspecies(ctx, IDSYM::UNION_TAG_NAME);
	    PooledString tag = get_name_name()->pooled_string();
	    IdentInfo *info;
	    if (idents->has_tag_binding(tag)) {
		info = idents->resolve_tag(tag);
		assert(info);
		if (info->type != IdentInfo::UNION_TAG)
		    set_semant_error(this, "`%s' is a %s tag, not a union tag",
				     tag.chars(),
				     info->type == IdentInfo::STRUCT_TAG ?
				     "struct" : "enum");
		else if (info->defined && info->definition != this)
		    set_semant_error(this,"Duplicate definition of union `%s'",
				     tag.chars());
		else if (info->definition && !info->defined) {
		    StructReferenceSpec *ref
		        = (StructReferenceSpec *)info->definition;
		    ref->completion.set_value(vg(), this);
		    ref->compute_synth_attrs(true);
		}
	    } else {
		info = new IdentInfo(tag, IdentInfo::UNION_TAG, this);
		idents->add_tag(tag, info);
	    }
	    info->definition = this;
	    info->defined = true;
	}
	Localizer<DeclContext> save(&ctx->decl_cx);
	ctx->decl_cx = STRUCT_UNION_CX;
	Localizer<SingleIdentTable *> save2(&ctx->aggr_members);
	ctx->aggr_members = new SingleIdentTable();
	for (decls_iterator i = get_decls(); i; ++i) {
	    if (!i.get_decl()->resolve_idents(ctx, idents))
		return false;
	}
	return true;
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> STRUCT IDSYM:name 
operator StructReferenceSpec extends ParentNode, type_spec, GLRStateMixin {
    public versioned slot Vptr definition = NULL;
    versioned Semantics attribute definition
	= TypedVal((Node*)definition.value(vg(), gvid));

    public versioned slot Vptr completion = NULL;
    versioned Semantics attribute completion
	= TypedVal((Node*)completion.value(vg(), gvid));

    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	PooledString tag = get_name()->pooled_string();
	get_name()->set_subspecies(ctx, IDSYM::STRUCT_TAG_NAME);
	IdentInfo *info = idents->resolve_tag(tag);
	if (info) {
	    if (info->type != IdentInfo::STRUCT_TAG)
		set_semant_error(this, "`%s' is a %s tag, not a struct tag",
				 tag.chars(),
				 info->type == IdentInfo::UNION_TAG ?
				 "union" : "enum");
	    definition.set_value(vg(), info->definition);
	    compute_synth_attrs(true);
	} else {
	    IdentInfo *info = new IdentInfo(tag, IdentInfo::STRUCT_TAG, this);
	    idents->add_tag(tag, info);
	}
	return true;
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> UNION IDSYM:name 
operator UnionReferenceSpec extends ParentNode, type_spec, GLRStateMixin {
    public versioned slot Vptr definition = NULL;
    versioned Semantics attribute definition
	= TypedVal((Node*)definition.value(vg(), gvid));

    public versioned slot Vptr completion = NULL;
    versioned Semantics attribute completion
	= TypedVal((Node*)completion.value(vg(), gvid));

    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	get_name()->set_subspecies(ctx, IDSYM::UNION_TAG_NAME);
	PooledString tag = get_name()->pooled_string();
	IdentInfo *info = idents->resolve_tag(tag);
	if (info) {
	    if (info->type != IdentInfo::UNION_TAG)
		set_semant_error(this, "`%s' is a %s tag, not a union tag",
				 tag.chars(),
				 info->type == IdentInfo::STRUCT_TAG ?
				 "struct" : "enum");
	    definition.set_value(vg(), info->definition);
	    compute_synth_attrs(true);
	} else {
	    IdentInfo *info = new IdentInfo(tag, IdentInfo::UNION_TAG, this);
	    idents->add_tag(tag, info);
	}
	return true;
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> ENUM OptR29E1:name '{' SeqR33E3:enums OptR33E4 '}' 
operator DefinedEnumSpec extends ParentNode, type_spec, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {	
	if (has_name()) {
	    get_name_name()->set_subspecies(ctx, IDSYM::ENUM_TAG_NAME);
	    PooledString tag = get_name_name()->pooled_string();
	    if (idents->has_tag_binding(tag)) {
		set_semant_error(this, "Aggregate tag `%s' redefined",
				 tag.chars());
	    } else {
		IdentInfo *info = new IdentInfo(tag, IdentInfo::ENUM_TAG,
						this);
		idents->add_tag(tag, info);
	    }
	}
	for (enums_iterator i = get_enums(); i; ++i) {
	    if (!i.get_enum_()->resolve_idents(ctx, idents))
		return false;
	} 
	return true;
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> ENUM IDSYM:name 
operator ReferencedEnumSpec extends ParentNode, type_spec, GLRStateMixin {
    public versioned slot Vptr definition = NULL;
    versioned Semantics attribute definition
	= TypedVal((Node*)definition.value(vg(), gvid));

    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	get_name()->set_subspecies(ctx, IDSYM::ENUM_TAG_NAME);
	PooledString tag = get_name()->pooled_string();
	IdentInfo *info = idents->resolve_tag(tag);
	if (!info) {
	    /* GCC allows you to forward declare enums, but the C99
               standard doesn't */
	    set_semant_error(this, "Undefined enum `%s'", tag.chars());
	} else if (info->type != IdentInfo::ENUM_TAG) {
	    set_semant_error(this, "`%s' is a %s tag, not an enum tag",
			     tag.chars(), info->type == IdentInfo::STRUCT_TAG ?
			                  "struct" : "union");
	} else {
	    definition.set_value(vg(), info->definition);
	    compute_synth_attrs(true);
	}
	return true;
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	type->uses_nontypename = true;
    }
}

// type_spec -> IDSYM:type_name 
operator TypedefTypeSpec extends ParentNode, type_spec, GLRStateMixin {
    public versioned slot Vptr definition = NULL;
    versioned Semantics attribute definition
	= TypedVal((Node*)definition.value(vg(), gvid));

    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	PooledString id = get_type_name()->pooled_string();
	get_type_name()->set_subspecies(ctx, IDSYM::TYPEDEF_NAME);
// 	cerr << "Before TTS, idents are:" << endl;
// 	idents->dump(cerr);
	if (!idents->resolve_ident(id)) {
	    assert(!idents->has_ident_binding(id));
	    ambig_semant_error(ctx, this, "undefined typedef name"
			       " %s", id.chars());
	    return !ctx->inside_ambig;
	}
// 	cerr << "In the middle of TTS, idents are:" << endl;
// 	idents->dump(cerr);
	IdentInfo *info = idents->resolve_ident(id);
	assert(info);
	if (info->type != IdentInfo::TYPEDEF_ID) {
	    ambig_semant_error(ctx, this, "identifier is not "
			       "a typedef name: %s", id.chars());
	    return !ctx->inside_ambig;
	} else {
// 	    cerr << "After TTS, idents are:" << endl;
// 	    idents->dump(cerr);
	    definition.set_value(vg(), info->definition);
	    compute_synth_attrs(true);
	    return true;
	}
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	if (type->uses_typename)
	    type->multiple_typename = true;
	else
	    type->uses_typename = true;
    }
}

operator type_spec_sym extends AmbigNode, type_spec, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	error("Unexpected ambiguity in type_spec::get_resolve_type\n");	
    }
}

phylum type_qual depends on decl {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	/* nothing */
    }
}

operator type_qual_sym extends AmbigNode, type_qual, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
 	error("Unexpected ambiguity in type qualifier!\n");
    }
}

phylum type_name depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// type_name -> SeqR14E0:spec_quals OptR14E1:declr 
operator TypeName extends ParentNode, type_name, GLRStateMixin
depends on Declaration {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	decl::ResolveType base_type;
	for (spec_quals_iterator i = get_spec_quals(); i; ++i) {
	    if (!i.get_spec_qual()->resolve_idents(ctx, idents))
		return false;
	    i.get_spec_qual()->get_resolve_type(ctx, &base_type);
	}
	if (!Declaration::check_resolve_type(ctx, &base_type, this))
	    return false;
	if (has_declr())
	    return get_declr_declr()->resolve_idents(ctx, idents);
	else
	    return true;
    }
}

operator type_name_sym extends AmbigNode, type_name, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in type name!\n");
	return true;
    }
}

phylum type_spec_qual depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	error("Unexpected call to type_spec_qual::get_resolve_type\n");
    }
}

// type_spec_qual -> type_spec:spec 
operator TypeSpecifier extends ParentNode, type_spec_qual, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_spec()->resolve_idents(ctx, idents);
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	get_spec()->get_resolve_type(ctx, type);
    }
}

// type_spec_qual -> type_qual:qual 
operator TypeQualifier extends ParentNode, type_spec_qual, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	/* nothing to do */
    }
}

operator no_type_spec_qual extends Node, type_spec_qual, GLRStateMixin {
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
    }
}

operator type_spec_qual_sym extends AmbigNode, type_spec_qual, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in type specifier/qualifier\n");
	return true;
    }
    public virtual method void get_resolve_type(ResolveContext *ctx,
						decl::ResolveType *type) {
	error("Unexpected ambiguity in type specifier/qualifier\n");
    }
}

phylum abst_declr {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// abst_declr -> OptR47E0:ptr direct_abst_declr:direct 
operator DirectAbstDecl extends ParentNode, abst_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_direct()->resolve_idents(ctx, idents);
    }
}

operator abst_declr_sym extends AmbigNode, abst_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in abst_declr\n");
	return true;
    }
}

phylum struct_decl depends on decl {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected call to struct_decl::resolve_idents()\n");
	return true;
    }
}

// struct_decl -> SeqR14E0:spec_quals SeqR62E1:declrs ';' 
operator StructDecl extends ParentNode, struct_decl, GLRStateMixin
depends on Declaration {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	decl::ResolveType base_type;
	for (spec_quals_iterator i = get_spec_quals(); i; ++i) {
	    if (!i.get_spec_qual()->resolve_idents(ctx, idents))
		return false;
	    i.get_spec_qual()->get_resolve_type(ctx, &base_type);
	}
	if (!Declaration::check_resolve_type(ctx, &base_type, this))
	    return false;
	for (declrs_iterator i = get_declrs(); i; ++i) {
	    if (!i.get_declr()->resolve_idents(ctx, idents, &base_type))
		return false;
	}
	return true;
    }
}

// struct_decl -> PREPROC
operator PreprocMember extends ParentNode, struct_decl
depends on Declaration {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	set_semant_error(this, "Can't run semantics with preprocessor "
			 "directives");
	return true;
    }
}    

operator no_struct_decl extends Node, struct_decl, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

operator struct_decl_sym extends AmbigNode, struct_decl, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return resolve_idents_func<struct_decl, struct_decl_sym>(ctx, idents,
								 this);
    }
}

phylum enum_ {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected call to enum_::resolve_idents\n");
	return true;
    }
}

// enum_ -> IDSYM:name OptR46E1:init 
operator EnumerationItem extends ParentNode, enum_, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (has_init() && !get_init_val()->resolve_idents(ctx, idents))
	    return false;
	IDSYM *ident = get_name();
	PooledString id_name = ident->pooled_string();
	ident->set_subspecies(ctx, IDSYM::ENUM_CONST_DECL_NAME);
	if (idents->has_ident_binding(id_name)) {
	    set_semant_error(this, "Identifier `%s' redefined as enumeration"
			     " constant", id_name.chars());
	} else {
	    IdentInfo *info = new IdentInfo(id_name, IdentInfo::ENUM_CONST,
					    this);
	    idents->add_ident(id_name, info);
	}
	return true;
    }
}

operator no_enum_ extends Node, enum_, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

operator enum__sym extends AmbigNode, enum_, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in enumeration member declaration\n");
	return false;
    }
}

phylum init_ depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// init_ -> assign_expr 
operator ExprInit extends ParentNode, init_, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// init_ -> '{' SeqR45E1:inits OptR33E4 '}' 
operator BlockInit extends ParentNode, init_, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	for (inits_iterator i = get_inits(); i; ++i)
	    if (!i.get_init()->resolve_idents(ctx, idents))
		return false;
	return true;
    }
}

operator init__sym extends AmbigNode, init_, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in initializer!\n");
	return false;
    }
}

phylum designator {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;       
    }
}

// designator -> '[' expr:idx ']' 
operator ArrayElemDesignator extends ParentNode, designator, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_idx()->resolve_idents(ctx, idents);
    }
}

// designator -> '.' IDSYM:field 
operator FieldDesignator extends ParentNode, designator, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	get_field()->set_subspecies(ctx, IDSYM::AGGR_MEMBER_NAME);
	return true;
    }
}

operator designator_sym extends AmbigNode, designator, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in designator\n");
	return true;       
    }
}

phylum expr depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;       
    }
}

// expr -> expr:pred '?' expr:texpr ':' expr:fexpr 
operator TernaryOp extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx, 
					      IdentTable *idents) {
	if (!get_pred()->resolve_idents(ctx, idents))
	    return false;
	if (has_texpr() && !get_texpr_expr()->resolve_idents(ctx, idents))
	    return false;
	return get_fexpr()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left LOR expr:right 
operator LogicalOr extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left LAND expr:right 
operator LogicalAnd extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left '|' expr:right 
operator BitwiseOr extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left '^' expr:right 
operator BitwiseXor extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left '&' expr:right 
operator BitwiseAnd extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left EQ expr:right 
operator Equals extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left NE expr:right 
operator NotEquals extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left '<' expr:right 
operator LessThan extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left '>' expr:right 
operator GreaterThan extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left LE expr:right 
operator LessThanEquals extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left GE expr:right 
operator GreaterThanEquals extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left LSHIFT expr:right 
operator LeftShift extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left RSHIFT expr:right 
operator RightShift extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left '+' expr:right 
operator Add extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left '-' expr:right 
operator Subtract extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left '*' expr:right 
operator Multiply extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left '/' expr:right 
operator Divide extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:left '%' expr:right 
operator Modulo extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// expr -> '(' type_name:name ')' expr:expr 
operator TypeCast extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_name()->resolve_idents(ctx, idents))
	    return false;
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// expr -> '&' expr:expr 
operator AddressOf extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// expr -> '+' expr:expr 
operator Positive extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// expr -> '-' expr:expr 
operator Negate extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// expr -> '~' expr:expr 
operator BitwiseNegate extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx, 
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// expr -> '!' expr:expr 
operator LogicalNot extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// expr -> expr:func '(' SeqR128E2:exprs ')' 
operator FunctionCall extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_func()->resolve_idents(ctx, idents))
	    return false;
	for (exprs_iterator i = get_exprs(); i; ++i)
	    if (!i.get_expr()->resolve_idents(ctx, idents))
		return false;
	return true;
    }
}

// expr -> INC expr:expr 
operator PreIncrement extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// expr -> DEC expr:expr 
operator PreDecrement extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// expr -> expr INC 
operator PostIncrement extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// expr -> expr DEC 
operator PostDecrement extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// expr -> SIZEOF expr 
operator VarSizeof extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// expr -> SIZEOF '(' type_name ')' 
operator TypeSizeof extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	get_type()->resolve_idents(ctx, idents);
	return true;
    }
}

// expr -> '(' exprs:expr ')' 
operator ParenthExpr extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// expr -> int_const:val 
operator IntegerConstant extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// expr -> char_const:val 
operator CharacterConstant extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// expr -> float_const:val 
operator FloatingConstant extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// expr -> str_const:val 
operator StringConstant extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// expr -> '(' type_name ')' '{' SeqR45E1:inits OptR33E4 '}' 
operator CompoundLiteral extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_type()->resolve_idents(ctx, idents))
	    return false;
	for (inits_iterator i = get_inits(); i; ++i)
	    if (!i.get_init()->resolve_idents(ctx, idents))
		return false;
	return true;
    }
}

// expr -> IDSYM:name 
operator VariableExpr extends ParentNode, expr, GLRStateMixin depends on decl {
    public versioned slot Vptr declarator = NULL;
    versioned Semantics attribute declarator
	= TypedVal((Node*)declarator.value(vg(), gvid));

    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	PooledString ident = get_name()->pooled_string();
	IdentInfo *info;
	if ((info = idents->resolve_ident(ident))) {
	    switch (info->type) {
	    case IdentInfo::TYPEDEF_ID:
		get_name()->set_subspecies(ctx, IDSYM::TYPEDEF_NAME);
		ambig_semant_error(ctx, this, "tried to use "
				   "typedef name %s in expression",
				   ident.chars());
		return !ctx->inside_ambig;
	    case IdentInfo::ENUM_CONST:
		get_name()->set_subspecies(ctx, IDSYM::ENUM_CONST_NAME);
		break;
	    case IdentInfo::OBJECT_ID:
		get_name()->set_subspecies(ctx, IDSYM::PLAIN_NAME);
		declarator.set_value(vg(), info->definition);
		compute_synth_attrs(true);
		break;
	    default:
		error("Unexpected type for identifier");
	    }
	} else {
	    set_semant_error(this, "Undefined identifier `%s'",
			     ident.chars());
	}
	return true;
    }
}

// expr -> expr '.' IDSYM:field 
operator StructAccess extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	get_expr()->resolve_idents(ctx, idents);
	get_field()->set_subspecies(ctx, IDSYM::AGGR_MEMBER_NAME);
	// resolve field?
	return true;
    }
}

// expr -> expr PTR IDSYM:field 
operator PointerAccess extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	get_expr()->resolve_idents(ctx, idents);
	get_field()->set_subspecies(ctx, IDSYM::AGGR_MEMBER_NAME);
	// resolve field?
	return true;
    }
}

// expr -> expr '[' exprs:index ']' 
operator ArrayAccess extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_expr()->resolve_idents(ctx, idents))
	    return false;
	return get_index()->resolve_idents(ctx, idents);
    }
}

// expr -> '*' expr:expr 
operator PointerDereference extends ParentNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

operator expr_sym extends AmbigNode, expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return resolve_idents_func<expr, expr_sym>(ctx, idents, this);
    }
}

phylum designation {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }    
}

// designation -> SeqR42E0:desigs '=' 
operator Designation extends ParentNode, designation, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	for (desigs_iterator i = get_desigs();i ; ++i)
	    if (!i.get_desig()->resolve_idents(ctx, idents))
		return false;
	return true;
    }    
}

operator designation_sym extends AmbigNode, designation, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in initializer designator\n");
	return true;
    }    
}

phylum inner_init {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }    
}

// inner_init -> OptR43E0:desig init_:init 
operator InnerInitializer extends ParentNode, inner_init, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (has_desig() && !get_desig_desig()->resolve_idents(ctx, idents))
	    return false;
	return get_init()->resolve_idents(ctx, idents);
    }    
}

operator inner_init_sym extends AmbigNode, inner_init, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in inner initializer\n");
	return true;
    }    
}

phylum arg_expr depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// arg_expr -> assign_expr:expr
operator ExpressionArgument extends arg_expr {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

// arg_expr -> type_name:type
operator FakeTypeArgument extends arg_expr {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	ambig_semant_error(ctx, this, "only macros can take type arguments");
	return !ctx->inside_ambig;
    }
}

operator arg_expr_sym extends AmbigNode, arg_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return resolve_idents_func<arg_expr, arg_expr_sym>(ctx, idents, this);
    }
}


phylum assign_expr depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// assign_expr -> expr:left '=' assign_expr:right 
operator NormalAssign extends ParentNode, assign_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// assign_expr -> expr:left MULASSIGN assign_expr:right 
operator MultiplyAssign extends ParentNode, assign_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// assign_expr -> expr:left DIVASSIGN assign_expr:right 
operator DivideAssign extends ParentNode, assign_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// assign_expr -> expr:left MODASSIGN assign_expr:right 
operator ModAssign extends ParentNode, assign_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// assign_expr -> expr:left ADDASSIGN assign_expr:right 
operator AddAssign extends ParentNode, assign_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// assign_expr -> expr:left SUBASSIGN assign_expr:right 
operator SubtractAssign extends ParentNode, assign_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// assign_expr -> expr:left SHLASSIGN assign_expr:right 
operator ShiftLeftAssign extends ParentNode, assign_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// assign_expr -> expr:left SHRASSIGN assign_expr:right 
operator ShiftRightAssign extends ParentNode, assign_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// assign_expr -> expr:left ANDASSIGN assign_expr:right 
operator AndAssign extends ParentNode, assign_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// assign_expr -> expr:left XORASSIGN assign_expr:right 
operator XorAssign extends ParentNode, assign_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// assign_expr -> expr:left ORASSIGN assign_expr:right 
operator OrAssign extends ParentNode, assign_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// assign_expr -> expr:expr 
operator SimpleExpression extends ParentNode, assign_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

operator assign_expr_sym extends AmbigNode, assign_expr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in expression!\n");
	return false;
    }
}

phylum direct_abst_declr {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// direct_abst_declr -> '(' abst_declr:declr ')' 
operator ParenAbstDecl extends ParentNode, direct_abst_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_declr()->resolve_idents(ctx, idents);
    }
}

// direct_abst_declr -> OptR58E0:declr '[' OptR58E2:expr ']' 
operator ArrayAbstDecl extends ParentNode, direct_abst_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (has_declr() && !get_declr_declr()->resolve_idents(ctx, idents))
	    return false;
	if (has_expr() && !get_expr_expr()->resolve_idents(ctx, idents))
	    return false;
	return true;
    }
}

// direct_abst_declr -> OptR58E0:declr '[' '*' ']' 
operator VariableAbstDecl 
extends ParentNode, direct_abst_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (has_declr() && !get_declr_declr()->resolve_idents(ctx, idents))
	    return false;
	return true;
    }
}

// direct_abst_declr -> direct_abst_declr:declr '(' OptR60E2:params ')' 
operator NestedFuncAbstDecl 
extends ParentNode, direct_abst_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_declr()->resolve_idents(ctx, idents))
	    return false;
	Localizer<DeclContext> save(&ctx->decl_cx);
	ctx->decl_cx = DECL_PARAM;
	NestedIdentTable param_table;
	param_table.add_parent(idents);
	if (has_params())
	    return get_params_params()->resolve_idents(ctx, &param_table);
	else
	    return true;
    }
}

// direct_abst_declr -> '(' OptR60E2:params ')' 
operator FuncAbstDecl extends ParentNode, direct_abst_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	Localizer<DeclContext> save(&ctx->decl_cx);
	ctx->decl_cx = DECL_PARAM;
	NestedIdentTable param_table;
	param_table.add_parent(idents);
	if (has_params())
	    return get_params_params()->resolve_idents(ctx, &param_table);
	else
	    return true;
    }
}

operator direct_abst_declr_sym 
extends AmbigNode, direct_abst_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in direct_abst_declr\n");
	return true;
    }
}

phylum param_type_list depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
    public virtual method void declare_ansi_idents(ResolveContext *ctx,
						   IdentTable *func_idents) {
	error("Unexpected call to param_type_list::declare_ansi_idents\n");
    }
}

// param_type_list -> SeqR68E0:decls OptR68E1:dotdotdot 
operator ParameterTypeList extends ParentNode, param_type_list, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	for (decls_iterator i = get_decls(); i; ++i) {
	    if (!i.get_decl()->resolve_idents(ctx, idents))
		return false;
	}
	return true;
    }
    public virtual method void declare_ansi_idents(ResolveContext *ctx,
						   IdentTable *func_idents) {
	bool first_param = true;
	for (decls_iterator i = get_decls(); i; ++i) {
	    i.get_decl()->declare_ansi_idents(ctx, func_idents);
	    first_param = false;
	}
    }
}

operator param_type_list_sym 
extends AmbigNode, param_type_list, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in parameter list!\n");
	return false;
    }
    public virtual method void declare_ansi_idents(ResolveContext *ctx,
						   IdentTable *func_idents) {
	error("Unexpected ambiguity in parameter list!\n");
    }
}

phylum struct_declr depends on decl {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      decl::ResolveType *base_type) {
	error("Unexpected call to struct_declr::resolve_idents\n");
	return true;
    }
}

// struct_declr -> declr:declr 
operator StructDeclr extends ParentNode, struct_declr, GLRStateMixin depends on decl {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      decl::ResolveType *base_type) {
	return get_declr()->resolve_idents(ctx, idents, base_type);
    }
}

// struct_declr -> OptR64E0:declr ':' expr:expr 
operator BitfieldDeclr extends ParentNode, struct_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      decl::ResolveType *base_type) {
	if (has_declr()) {	    
	    if (!get_declr_declr()->resolve_idents(ctx, idents, base_type))
		return false;
	}
	return get_expr()->resolve_idents(ctx, idents);
    }
}

operator no_struct_declr extends Node, struct_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      decl::ResolveType *base_type) {
	return true;
    }
}

operator struct_declr_sym extends AmbigNode, struct_declr, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents,
					      decl::ResolveType *base_type) {
	error("Unexpected ambiguity in structure member declarator\n");
	return true;
    }
}

phylum param_decl depends on trans_unit, decl {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
    public virtual method void declare_ansi_idents(ResolveContext *ctx, 
						   IdentTable *func_idents) {
	error("Unexpected call to param_type_list::declare_ansi_idents\n");
    }

}

// param_decl -> SeqR1E0:specs declr:declr 
operator NamedParamDecl extends ParentNode, param_decl, GLRStateMixin
depends on Declaration {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	decl::ResolveType base_type;
	for (specs_iterator i = get_specs(); i; ++i) {
	    if (!i.get_spec()->resolve_idents(ctx, idents))
		return false;
	    i.get_spec()->get_resolve_type(ctx, &base_type);
	}
	if (!Declaration::check_resolve_type(ctx, &base_type, this))
	    return false;
	return get_declr()->resolve_idents(ctx, idents, &base_type);
    }
    public virtual method void declare_ansi_idents(ResolveContext *ctx,
						   IdentTable *func_idents) {
	get_declr()->declare_ident(ctx, func_idents);
    }
}

// param_decl -> SeqR1E0:specs OptR14E1:declr 
operator UnnamedParamDecl extends ParentNode, param_decl, GLRStateMixin
depends on Declaration {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	decl::ResolveType base_type;
	for (specs_iterator i = get_specs(); i; ++i) {
	    if (!i.get_spec()->resolve_idents(ctx, idents))
		return false;
	    i.get_spec()->get_resolve_type(ctx, &base_type);
	    // XXX This still allows foo(void, void) {}
	}
	if (!Declaration::check_resolve_type(ctx, &base_type, this))
	    return false;
	if (has_declr()) {
	   return get_declr_declr()->resolve_idents(ctx, idents);
	}
	return true;
    }
    public virtual method void declare_ansi_idents(ResolveContext *ctx, 
						   IdentTable *func_idents) {
    }
}

operator param_decl_sym extends AmbigNode, param_decl, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return resolve_idents_func<param_decl, param_decl_sym>(ctx, idents,
							       this);
    }
    public virtual method void declare_ansi_idents(ResolveContext *ctx,
						   IdentTable *func_idents) {
	param_decl::phylum_cast(primary_alternative())
	    ->declare_ansi_idents(ctx, func_idents);
    }
}

phylum for_init depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// for_init -> OptR69E0:expr ';' 
operator ExpressionInit extends ParentNode, for_init, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (has_expr())
	    return get_expr_expr()->resolve_idents(ctx, idents);
	return true;
    }
}

// for_init -> decl:decl 
operator DeclarationInit extends ParentNode, for_init, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	Localizer<DeclContext> save(&ctx->decl_cx);
	ctx->decl_cx = FOR_INIT;
	return get_decl()->resolve_idents(ctx, idents);
    }
}

operator for_init_sym extends AmbigNode, for_init, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return resolve_idents_func<for_init, for_init_sym>(ctx, idents, this);
    }

}

phylum exprs depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// exprs -> exprs:left ',' assign_expr:right 
operator CommaList extends ParentNode, exprs, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_left()->resolve_idents(ctx, idents))
	    return false;
	return get_right()->resolve_idents(ctx, idents);
    }
}

// exprs -> assign_expr:expr 
operator SingleExpr extends ParentNode, exprs, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_expr()->resolve_idents(ctx, idents);
    }
}

operator exprs_sym extends AmbigNode, exprs, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in expressions!\n");
	return false;
    }
}

phylum stmt depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// stmt -> IDSYM:label ':' stmt:stmt 
operator LabeledStatement extends ParentNode, stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	PooledString label = get_label()->pooled_string();	
	get_label()->set_subspecies(ctx, IDSYM::LABEL_DECL_NAME);
	LabelInfo *info;
	if (!ctx->labels->has_binding(label)) {
	    info = new LabelInfo(label);
	    ctx->labels->add(label, info);
	} else {
	    info = ctx->labels->resolve(label);
	}
	info->defs.push_back(this);
	return get_stmt()->resolve_idents(ctx, idents);
    }
}

// stmt -> CASE expr:expr ':' stmt:stmt 
operator CaseLabelStatement extends ParentNode, stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_expr()->resolve_idents(ctx, idents))
	    return false;
	return get_stmt()->resolve_idents(ctx, idents);
    }
}

// stmt -> DEFAULT ':' stmt:stmt 
operator DefaultLabelStatement extends ParentNode, stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_stmt()->resolve_idents(ctx, idents);
    }
}

// stmt -> OptR69E0:expr ';' 
operator ExprStatement extends ParentNode, stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (has_expr())
	    return get_expr_expr()->resolve_idents(ctx, idents);
	return true;
    }
}

// stmt -> comp_stmt:stmt 
operator CompoundStatement extends ParentNode, stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_stmt()->resolve_idents(ctx, idents);
    }
}

// stmt -> IF '(' exprs:pred ')' stmt:t_stmt 
operator IfNoElseStatement extends ParentNode, stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_pred()->resolve_idents(ctx, idents))
	    return false;
	return get_t_stmt()->resolve_idents(ctx, idents);
    }
}

// stmt -> IF '(' exprs:pred ')' stmt:t_stmt ELSE stmt:f_stmt 
operator IfElseStatement extends ParentNode, stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_pred()->resolve_idents(ctx, idents))
	    return false;
	if (!get_t_stmt()->resolve_idents(ctx, idents))
	    return false;
	return get_f_stmt()->resolve_idents(ctx, idents);
    }
}

// stmt -> SWITCH '(' exprs:val ')' stmt:stmt 
operator SwitchStatement extends ParentNode, stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_val()->resolve_idents(ctx, idents))
	    return false;
	return get_stmt()->resolve_idents(ctx, idents);
    }
}

// stmt -> WHILE '(' exprs:val ')' stmt:stmt 
operator WhileStatement extends ParentNode, stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_val()->resolve_idents(ctx, idents))
	    return false;
	return get_stmt()->resolve_idents(ctx, idents);
    }
}

// stmt -> DO stmt:stmt WHILE '(' exprs:val ')' ';' 
operator DoWhileStatement extends ParentNode, stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_stmt()->resolve_idents(ctx, idents))
	    return false;
	return get_val()->resolve_idents(ctx, idents);
    }
}

// stmt -> FOR '(' for_init:init OptR69E0:pred ';' OptR69E0:inc ')' stmt:stmt 
operator ForStatement extends ParentNode, stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (!get_init()->resolve_idents(ctx, idents))
	    return false;
	if (has_pred() && !get_pred_expr()->resolve_idents(ctx, idents))
	    return false;
	if (has_inc() && !get_inc_expr()->resolve_idents(ctx, idents))
	    return false;
	return get_stmt()->resolve_idents(ctx, idents);
    }
}

// stmt -> GOTO IDSYM:label ';' 
operator GotoStatement extends ParentNode, stmt, GLRStateMixin {
    public versioned slot Vptr target = NULL;
    versioned Semantics attribute target
	= TypedVal((Node*)target.value(vg(), gvid));

    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	PooledString label = get_label()->pooled_string();	
	get_label()->set_subspecies(ctx, IDSYM::LABEL_NAME);
	LabelInfo *info;
	if (!ctx->labels->has_binding(label)) {
	    info = new LabelInfo(label);
	    ctx->labels->add(label, info);
	} else {
	    info = ctx->labels->resolve(label);
	}
	info->uses.push_back(this);
	return true;
    }
}

// stmt -> RETURN OptR69E0:ret ';' 
operator ReturnStatement extends ParentNode, stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	if (has_ret())
	    return get_ret_expr()->resolve_idents(ctx, idents);
	return true;
    }
}

operator stmt_sym extends AmbigNode, stmt, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	error("Unexpected ambiguity in statement!\n");
	return true;
    }
}

phylum block_item depends on trans_unit {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return true;
    }
}

// block_item -> decl:decl 
operator DeclarationItem extends ParentNode, block_item, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	Localizer<DeclContext> save(&ctx->decl_cx);
	ctx->decl_cx = BLOCK;
	return get_decl()->resolve_idents(ctx, idents);
    }
}

// block_item -> stmt:stmt 
operator StatementItem extends ParentNode, block_item, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return get_stmt()->resolve_idents(ctx, idents);
    }
}

operator block_item_sym extends AmbigNode, block_item, GLRStateMixin {
    public virtual method bool resolve_idents(ResolveContext *ctx,
					      IdentTable *idents) {
	return resolve_idents_func<block_item, block_item_sym>(ctx, idents,
							       this);
    }
}

operator IDSYM  {
    public enum NameType {
	PLAIN_NAME = 0,
	DECLARATION_NAME,
	TYPEDEF_NAME,
	TYPEDEF_DECL_NAME,
	LABEL_NAME,
	LABEL_DECL_NAME,
	STRUCT_TAG_NAME,
	UNION_TAG_NAME,
	ENUM_TAG_NAME,    
	AGGR_MEMBER_NAME,
	AGGR_MEMBER_DECL_NAME,
	ENUM_CONST_NAME,
	ENUM_CONST_DECL_NAME,
	PARAM_DECL_NAME,
	KNR_PARAM_LIST_NAME,
	KNR_PARAM_DECL_NAME
    };

    // Note -- this enum has to be kept in sync with the definition
    // of harmonia-c-idsym-face-array in harmonia-c.el
    public static slot const char *subspecies_names[] = {
	"Plain",
	"Declaration",
	"Typedef",
	"TypedefDecl",
	"Label",
	"LabelDecl",
	"StructTag",
	"UnionTag",
	"EnumTag",
	"AggregateMember",
	"AggregateMemberDecl",
	"EnumerationConstant",
	"EnumerationConstantDecl",
	"ParameterDecl",
	"K&RParameterList",
	"K&RParameterDecl"};

    public versioned slot Vint subspecies = 0;
    versioned Semantics attribute subspecies
	= TypedVal(subspecies.value(vg(), gvid));
    versioned Semantics attribute subspecies_name
	= TypedVal(subspecies_names[subspecies.value(vg(), gvid)]); 

    public virtual method void set_subspecies(ResolveContext *ctx, int type) {
	if (ctx->inside_ambig) {
	    ctx->deferred_classifications
		->push_back(pair<IDSYM*,int>(this, type));
	} else {
	    subspecies.set_value(vg(), type);
	    compute_synth_attrs(true);
	}
    }
}