// -*- mode: java; c-basic-offset: 4 -*-
//
// Copyright 2001-2002 by the Regents of the University of California.
// All rights reserved.
//
//      HARMONIA: C language module type checking
//
// RCS Info: $Revision: 1.1 $ on $Date: 2003/05/12 01:49:33 $
//           $Source: /home/cs/harmonia/cvsroot/www/harmonia/projects/langs/c/c-typecheck.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/node/UltraRoot.h"

include "c.h"

phylum trans_unit {
    public virtual method void check_types(TypecheckContext *ctx) {
    }
}

// trans_unit -> SeqR0E0star:ext_decls 
operator TranslationUnit extends trans_unit {
    public virtual method void check_types(TypecheckContext *ctx) {
	for (ext_decls_iterator i = get_ext_decls(); i; ++i)
	    i.get_ext_decl()->check_types(ctx);
    }
}

operator no_trans_unit extends trans_unit {
}

operator trans_unit_sym extends trans_unit {
    public virtual method void check_types(TypecheckContext *ctx) {
	trans_unit::phylum_cast(primary_alternative())->check_types(ctx);
    }
}

phylum ext_decl {
    public virtual method void check_types(TypecheckContext *ctx) {
    }
}

// ext_decl -> SeqR1E0star:specs declr:declr SeqR1E2star:decls comp_stmt:body 
operator FunctionDefinition extends ext_decl {
    public virtual method void check_types(TypecheckContext *ctx) {
	decl::TypeInfo base_type_info;
	for (specs_iterator i = get_specs(); i; ++i)
	    i.get_spec()->check_types(ctx);
	for (specs_iterator i = get_specs(); i; ++i)
	    i.get_spec()->specify(ctx, &base_type_info);
	CType *base_type = decl::assemble_base_type(ctx, this,
						    &base_type_info);
	CType *maybe_func_type = get_declr()->build_type(ctx, base_type);
	if (!maybe_func_type->is_function()) {
	    set_semant_error(this, "Function definition must have "
			     "function type, not %s",
			     maybe_func_type->name().c_str());
	    return;
	}
	FunctionType *func_type = (FunctionType *)maybe_func_type;
	CType *ret_type = func_type->return_type;	    
	Localizer<CType *> save(&ctx->return_type);
	ctx->return_type = ret_type;
	get_body()->check_types(ctx);
    }
}

// ext_decl -> decl:decl 
operator OtherExtDeclaration extends ext_decl {
    public virtual method void check_types(TypecheckContext *ctx) {
	get_decl()->check_types(ctx);
    }
}

operator no_ext_decl extends ext_decl {
}

operator ext_decl_sym extends ext_decl {
    public virtual method void check_types(TypecheckContext *ctx) {
	ext_decl::phylum_cast(primary_alternative())->check_types(ctx);
    }
}

phylum decl_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {}
    public virtual method void check_types(TypecheckContext *ctx) {}
}

// decl_spec -> stor_class_spec:spec 
operator StorageDeclSpec extends decl_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	get_spec()->specify(ctx, base_info);
    }
}

// decl_spec -> type_spec:spec 
operator TypeSpecDeclSpec extends decl_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	get_spec()->specify(ctx, base_info);
    }
    public virtual method void check_types(TypecheckContext *ctx) {
	get_spec()->check_types(ctx);
    }
}

// decl_spec -> type_qual:qual 
operator TypeQualDeclSpec extends decl_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	get_qual()->specify(base_info);
    }
}

// decl_spec -> INLINE:qual 
operator FunctionQualDeclSpec extends decl_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	// base_info->is_inline = true;
    }
}

operator no_decl_spec extends decl_spec {
}

operator decl_spec_sym extends decl_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	decl_spec::phylum_cast(primary_alternative())->specify(ctx, base_info);
    }
    public virtual method void check_types(TypecheckContext *ctx) {
	decl_spec::phylum_cast(primary_alternative())->check_types(ctx);
    }
}

phylum declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	return base_type;
    }
}

// declr -> OptR46E0:ptr direct_declr:direct 
operator Declarator extends declr {
    public slot CType *c_type;

    versioned Semantics attribute type_name
        = TypedVal(c_type ? strdup(c_type->name().c_str()) : "null");

    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	CType *type = base_type;
	if (has_ptr())
	    type = get_ptr_ptr()->point_to(ctx, type);
	type = get_direct()->build_type(ctx, type);
	Declarator *link = (Declarator *)linkage_link.value();
	if (link) {
	    CType *comp = CType::composite_type(link->c_type, type);
	    if (!comp)
		set_semant_error(this, "Identifier redefined with "
		                       "incompatible type %s (was %s)",
		                 type->name().c_str(),
                                 link->c_type->name().c_str());
	    else
		type = comp;
	} else if (decl_context == KnR_PARAM || decl_context == DEF_PARAM
	           || decl_context == DECL_PARAM) {
	    type = type->param_adjust();
	}
	c_type = type;
	return type;
    }
}

operator no_declr extends declr {
}

operator declr_sym extends declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	return declr::phylum_cast(primary_alternative())
	    ->build_type(ctx, base_type);
    }
}

phylum decl {
    public enum TypeBits {
	VOID = 1,
	CHAR = 2,
	SHORT = 4,
	INT = 8,
	LONG = 16,
	LONG_LONG = 32,
	FLOAT = 64,
	DOUBLE = 128,
	SIGNED = 256,
	UNSIGNED = 512,
	_BOOL = 1024,
	_COMPLEX = 2048,
	_IMAGINARY = 4096,
	STRUCT_UNION_ENUM = 8192,
	TYPEDEF_NAME = 16384,
	TYPEDEF_TYPEDEF = 32768
    };

    public enum AggrDeclType {
	NO_AGGREGATE,
	AGGREGATE_REF,
	AGGREGATE_DEF
    };

    public struct TypeInfo :  public Collectable {
	StorageClass storage;
	int spec_bits;
	AggrDeclType aggregate;
	unsigned int /* Qualifiers */ quals;
	CType *specified_type;

	TypeInfo() : storage(NO_STORAGE), spec_bits(0),
	    aggregate(NO_AGGREGATE), quals(0), specified_type(0) {}
    }

    public virtual method void check_types(TypecheckContext *ctx) {
    }

    public static method CType *assemble_base_type(TypecheckContext *ctx,
						   Node *node,
						   TypeInfo *info) {
	CType *base_type;
	if (info->spec_bits & (_COMPLEX|_IMAGINARY) &&
	    !(info->spec_bits & (FLOAT|DOUBLE)))
	    set_semant_error(node, "Only floating-point types can be "
			     "'_Complex' or '_Imaginary'");
	if (info->spec_bits & VOID) {
	    base_type = &ctx->the_VoidType;
	} else if (info->spec_bits & CHAR) {
	    if (info->spec_bits & SIGNED)
		base_type = &ctx->the_SignedCharType;
	    else if (info->spec_bits & UNSIGNED)
		base_type = &ctx->the_UnsignedCharType;
	    else
		base_type = &ctx->the_PlainCharType;
	} else if (info->spec_bits & SHORT) {
	    if (info->spec_bits & UNSIGNED)
		base_type = &ctx->the_UnsignedShortType;
	    else
		base_type = &ctx->the_ShortIntType;
	} else if (info->spec_bits & FLOAT) {
	    if (info->spec_bits & _COMPLEX)
		base_type = &ctx->the_ComplexFloatType;
	    else if (info->spec_bits & _IMAGINARY)
		base_type = &ctx->the_ImaginaryFloatType;
	    else
		base_type = &ctx->the_FloatType;
	} else if (info->spec_bits & DOUBLE) {
	    if (info->spec_bits & LONG) {
		if (info->spec_bits & _COMPLEX)
		    base_type = &ctx->the_ComplexLongDoubleType;
		else if (info->spec_bits & _IMAGINARY)
		    base_type = &ctx->the_ImaginaryLongDoubleType;
		else
		    base_type = &ctx->the_LongDoubleType;
	    } else {
		if (info->spec_bits & _COMPLEX)
		    base_type = &ctx->the_ComplexDoubleType;
		else if (info->spec_bits & _IMAGINARY)
		    base_type = &ctx->the_ImaginaryDoubleType;
		else
		    base_type = &ctx->the_DoubleType;
	    }
	} else if (info->spec_bits & LONG_LONG) {
	    if (info->spec_bits & UNSIGNED)
		base_type = &ctx->the_UnsignedLongLongType;
	    else
		base_type = &ctx->the_LongLongIntType;
	} else if (info->spec_bits & LONG) {
	    if (info->spec_bits & UNSIGNED)
		base_type = &ctx->the_UnsignedLongType;
	    else
		base_type = &ctx->the_LongIntType;
	} else if (info->spec_bits & INT) {
	    if (info->spec_bits & UNSIGNED)
		base_type = &ctx->the_UnsignedIntType;
	    else
		base_type = &ctx->the_SignedIntType; // XXX bitfields
	} else if (info->spec_bits & _BOOL) {
	    base_type = &ctx->the_BoolType;
	} else if (info->spec_bits & UNSIGNED) {
	    base_type = &ctx->the_UnsignedIntType;
	} else if (info->spec_bits & SIGNED) {
	    base_type = &ctx->the_SignedIntType;
	} else if (info->spec_bits & TYPEDEF_NAME) {
	    if (info->specified_type)
		base_type = info->specified_type;
	    else {
		set_semant_error(node, "Undefined typename");
		base_type = &ctx->the_UnimplementedType;
	    }
	} else if (info->spec_bits & STRUCT_UNION_ENUM) {
	    if (info->specified_type)
		base_type = info->specified_type;
	    else {
		base_type = &ctx->the_UnimplementedType;
	    }
	} else if (info->spec_bits) {
	    base_type = &ctx->the_UnimplementedType;
	} else {
	    set_semant_error(node, "C99 forbids a declaration with no type");
	    base_type = &ctx->the_SignedIntType;
	}
	base_type = base_type->qualified_by(info->quals);
	return base_type;
    }
}

// decl -> SeqR1E0star:specs SeqR7E1star:declrs SEMI 
operator Declaration extends decl {
    public virtual method void check_types(TypecheckContext *ctx) {
	TypeInfo base_type_info;
	for (specs_iterator i = get_specs(); i; ++i)
	    i.get_spec()->check_types(ctx);
	for (specs_iterator i = get_specs(); i; ++i)
	    i.get_spec()->specify(ctx, &base_type_info);
	CType *base_type = assemble_base_type(ctx, this, &base_type_info);
	//string msg = "Base type: " + base_type->name();
	//set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	//compute_synth_attrs(true);
	bool has_declrs = get_declrs();
	if (base_type_info.aggregate == NO_AGGREGATE && !has_declrs)
	    set_semant_error(this, "Declaration declares neither an aggregate"
			     " nor any declarators");	    
	for (declrs_iterator i = get_declrs(); i; ++i)
	    i.get_declr()->check_types(ctx, base_type);
    }
}

operator no_decl extends decl {
}

operator decl_sym extends decl {
    public virtual method void check_types(TypecheckContext *ctx) {
	decl::phylum_cast(primary_alternative())->check_types(ctx);
    }
}

phylum comp_stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
    }
}

// comp_stmt -> LBRACE SeqR87E1star:items RBRACE 
operator BlockStatement extends comp_stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	for (items_iterator i = get_items(); i; ++i)
	    i.get_item_()->check_types(ctx);
    }
}

operator no_comp_stmt extends comp_stmt {
}

operator comp_stmt_sym extends comp_stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	comp_stmt::phylum_cast(primary_alternative())->check_types(ctx);
    }
}

phylum stor_class_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
    }
}

// stor_class_spec -> AUTO 
operator AutoStrClassSpec extends stor_class_spec {
}

// stor_class_spec -> REGISTER 
operator RegisterStrClassSpec extends stor_class_spec {
}

// stor_class_spec -> STATIC 
operator StaticStrClassSpec extends stor_class_spec {
}

// stor_class_spec -> EXTERN 
operator ExternStrClassSpec extends stor_class_spec {
}

// stor_class_spec -> TYPEDEF 
operator TypedefStrClassSpec extends stor_class_spec {
}

operator no_stor_class_spec extends stor_class_spec {
}

operator stor_class_spec_sym extends stor_class_spec {
}

phylum type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	error("Unexpected call to type_spec::specify\n");
    }
    public virtual method void check_types(TypecheckContext *ctx) {}

    public virtual method StructUnionType *get_struct_union_type() {
	error("Unexpected call to type_spec::get_struct_union_type\n");
	return 0;
    }
}

// type_spec -> VOID 
operator VoidTypeSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits)
	    set_semant_error(this, "'void' is not allowed with "
			     "any other type specifier");
	else
	    base_info->spec_bits |= decl::VOID;
    }
}

// type_spec -> CHAR 
operator CharTypeSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits & decl::CHAR)
	    set_semant_error(this, "Too many 'char's in type");
	else if (base_info->spec_bits & decl::STRUCT_UNION_ENUM)
	    set_semant_error(this, "A struct, union, or enum specifier is not"
			     " allowed with any other type specifier");
	else if (base_info->spec_bits & decl::VOID)
	    set_semant_error(this, "'void' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::_BOOL)
	    set_semant_error(this, "'_Bool' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::INT)
	    set_semant_error(this, "'char' and 'int' are mutually exclusive");
	else if (base_info->spec_bits & decl::FLOAT)
	    set_semant_error(this, "'char' and 'float' are "
			     "mutually exclusive");
	else if (base_info->spec_bits & decl::DOUBLE)
	    set_semant_error(this, "'char' and 'double' are "
			     "mutually exclusive");
	else if (base_info->spec_bits & decl::SHORT)
	    set_semant_error(this, "'char's can't be 'short'");
	else if (base_info->spec_bits & decl::LONG)
	    set_semant_error(this, "'char's can't be 'long'");
	else if (base_info->spec_bits & decl::_COMPLEX)
	    set_semant_error(this, "Only floating-point types "
			     "can be '_Complex'");
	else if (base_info->spec_bits & decl::_IMAGINARY)
	    set_semant_error(this, "Only floating-point types "
			     "can be '_Imaginary'");
	else
	    base_info->spec_bits |= decl::CHAR;
    }
}

// type_spec -> SHORT 
operator ShortTypeSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits & decl::SHORT)
	    set_semant_error(this, "Too many 'short's in type");
	else if (base_info->spec_bits & decl::STRUCT_UNION_ENUM)
	    set_semant_error(this, "A struct, union, or enum specifier is not"
			     " allowed with any other type specifier");
	else if (base_info->spec_bits & decl::VOID)
	    set_semant_error(this, "'void' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::_BOOL)
	    set_semant_error(this, "'_Bool' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::LONG)
	    set_semant_error(this, "'short' and 'long' are "
			     "mutually exclusive");
	else if (base_info->spec_bits & decl::CHAR)
	    set_semant_error(this, "'char's can't be 'short'");
	else if (base_info->spec_bits & decl::_COMPLEX)
	    set_semant_error(this, "Only floating-point types "
			     "can be '_Complex'");
	else if (base_info->spec_bits & decl::_IMAGINARY)
	    set_semant_error(this, "Only floating-point types "
			     "can be '_Imaginary'");
	else
	    base_info->spec_bits |= decl::SHORT;
    }
}

// type_spec -> INT 
operator IntTypeSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits & decl::INT)
	    set_semant_error(this, "Too many 'int's in type");
	else if (base_info->spec_bits & decl::STRUCT_UNION_ENUM)
	    set_semant_error(this, "A struct, union, or enum specifier is not"
			     " allowed with any other type specifier");
	else if (base_info->spec_bits & decl::VOID)
	    set_semant_error(this, "'void' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::_BOOL)
	    set_semant_error(this, "'_Bool' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::CHAR)
	    set_semant_error(this, "'char' and 'int' are mutually exclusive");
	else if (base_info->spec_bits & decl::FLOAT)
	    set_semant_error(this, "'float' and 'int' are mutually exclusive");
	else if (base_info->spec_bits & decl::DOUBLE)
	    set_semant_error(this, "'double' and 'int' are "
			     "mutually exclusive");
	else if (base_info->spec_bits & decl::_COMPLEX)
	    set_semant_error(this, "Only floating-point types "
			     "can be '_Complex'");
	else if (base_info->spec_bits & decl::_IMAGINARY)
	    set_semant_error(this, "Only floating-point types "
			     "can be '_Imaginary'");
	else
	    base_info->spec_bits |= decl::INT;
    }
}

// type_spec -> LONG 
operator LongTypeSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits & decl::LONG_LONG)
	    set_semant_error(this, "Too many 'longs's in type");
	else if (base_info->spec_bits & decl::STRUCT_UNION_ENUM)
	    set_semant_error(this, "A struct, union, or enum specifier is not"
			     " allowed with any other type specifier");
	else if (base_info->spec_bits & decl::VOID)
	    set_semant_error(this, "'void' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::_BOOL)
	    set_semant_error(this, "'_Bool' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::SHORT)
	    set_semant_error(this, "'short' and 'long' are "
			     "mutually exclusive");
	else if (base_info->spec_bits & decl::CHAR)
	    set_semant_error(this, "'char's can't be 'long'");
	else if (base_info->spec_bits & decl::FLOAT)
	    set_semant_error(this, "'float's can't be 'long'");
	else if (base_info->spec_bits &
		   (decl::DOUBLE|decl::_COMPLEX|decl::_IMAGINARY) &&
		 base_info->spec_bits & decl::LONG)
	    set_semant_error(this, "Only integers can be 'long long'");
	else {
	    if (base_info->spec_bits & decl::LONG)
		base_info->spec_bits |= decl::LONG_LONG;
	    else
		base_info->spec_bits |= decl::LONG;
	}
    }
}

// type_spec -> FLOAT 
operator FloatTypeSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits & decl::FLOAT)
	    set_semant_error(this, "Too many 'float's in type");
	else if (base_info->spec_bits & decl::STRUCT_UNION_ENUM)
	    set_semant_error(this, "A struct, union, or enum specifier is not"
			     " allowed with any other type specifier");
	else if (base_info->spec_bits & decl::VOID)
	    set_semant_error(this, "'void' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::_BOOL)
	    set_semant_error(this, "'_Bool' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::CHAR)
	    set_semant_error(this, "'char' and 'float' are "
			     "mutually exclusive");
	else if (base_info->spec_bits & decl::INT)
	    set_semant_error(this, "'float' and 'int' are mutually exclusive");
	else if (base_info->spec_bits & decl::DOUBLE)
	    set_semant_error(this, "'float' and 'double' are "
			     "mutually exclusive");
	else if (base_info->spec_bits & decl::SHORT)
	    set_semant_error(this, "'float's can't be 'short'");
	else if (base_info->spec_bits & decl::LONG)
	    set_semant_error(this, "'float's can't be 'long'");
	else if (base_info->spec_bits & decl::SIGNED)
	    set_semant_error(this, "Floating point types can't be "
			     "declared 'signed'");
	else if (base_info->spec_bits & decl::UNSIGNED)
	    set_semant_error(this, "Floating point types can't be 'unsigned'");
	else
	    base_info->spec_bits |= decl::FLOAT;
    }
}

// type_spec -> DOUBLE 
operator DoubleTypeSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits & decl::DOUBLE)
	    set_semant_error(this, "Too many 'double's in type");
	else if (base_info->spec_bits & decl::STRUCT_UNION_ENUM)
	    set_semant_error(this, "A struct, union, or enum specifier is not"
			     " allowed with any other type specifier");
	else if (base_info->spec_bits & decl::VOID)
	    set_semant_error(this, "'void' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::_BOOL)
	    set_semant_error(this, "'_Bool' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::CHAR)
	    set_semant_error(this, "'char' and 'double' are "
			     "mutually exclusive");
	else if (base_info->spec_bits & decl::INT)
	    set_semant_error(this, "'double' and 'int' are "
			     "mutually exclusive");
	else if (base_info->spec_bits & decl::FLOAT)
	    set_semant_error(this, "'float' and 'double' are "
			     "mutually exclusive");
	else if (base_info->spec_bits & decl::SHORT)
	    set_semant_error(this, "'double's can't be 'short'");
	else if (base_info->spec_bits & decl::SIGNED)
	    set_semant_error(this, "Floating point types can't be "
			     "declared 'signed'");
	else if (base_info->spec_bits & decl::UNSIGNED)
	    set_semant_error(this, "Floating point types can't be 'unsigned'");
	else if (base_info->spec_bits & decl::LONG_LONG)
	    set_semant_error(this, "Only integers can be 'long long'");
	else
	    base_info->spec_bits |= decl::DOUBLE;
    }
}

// type_spec -> SIGNED 
operator SignedTypeSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits & decl::SIGNED)
	    set_semant_error(this, "Too many 'signed's in type");
	else if (base_info->spec_bits & decl::STRUCT_UNION_ENUM)
	    set_semant_error(this, "A struct, union, or enum specifier is not"
			     " allowed with any other type specifier");
	else if (base_info->spec_bits & decl::VOID)
	    set_semant_error(this, "'void' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::_BOOL)
	    set_semant_error(this, "'_Bool' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::UNSIGNED)
	    set_semant_error(this, "'signed' and 'unsigned' are mutually "
			     "exclusive");
	else if (base_info->spec_bits & (decl::FLOAT|decl::DOUBLE))
	    set_semant_error(this, "Floating point types can't be "
			     "declared 'signed'");
	else
	    base_info->spec_bits |= decl::SIGNED;
    }
}

// type_spec -> UNSIGNED 
operator UnsignedTypeSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits & decl::UNSIGNED)
	    set_semant_error(this, "Too many 'unsigned's in type");
	else if (base_info->spec_bits & decl::STRUCT_UNION_ENUM)
	    set_semant_error(this, "A struct, union, or enum specifier is not"
			     " allowed with any other type specifier");
	else if (base_info->spec_bits & decl::VOID)
	    set_semant_error(this, "'void' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::_BOOL)
	    set_semant_error(this, "'_Bool' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::SIGNED)
	    set_semant_error(this, "'signed' and 'unsigned' are mutually "
			     "exclusive");
	else if (base_info->spec_bits & (decl::FLOAT|decl::DOUBLE))
	    set_semant_error(this, "Floating point types can't be 'unsigned'");
	else
	    base_info->spec_bits |= decl::UNSIGNED;
    }
}

// type_spec -> _BOOL 
operator BooleanTypeSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits)
	    set_semant_error(this, "'_Bool' is not allowed with "
			     "any other type specifier");
	else
	    base_info->spec_bits |= decl::_BOOL;
    }
}

// type_spec -> _COMPLEX 
operator ComplexTypeSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits & decl::_COMPLEX)
	    set_semant_error(this, "Too many '_Complex'es in type");
	else if (base_info->spec_bits & decl::STRUCT_UNION_ENUM)
	    set_semant_error(this, "A struct, union, or enum specifier is not"
			     " allowed with any other type specifier");
	else if (base_info->spec_bits & decl::VOID)
	    set_semant_error(this, "'void' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::_BOOL)
	    set_semant_error(this, "'_Bool' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::_IMAGINARY)
	    set_semant_error(this, "'_Complex' and '_Imaginary' are "
			     "mutually exclusive");
	else if (base_info->spec_bits & (decl::CHAR|decl::INT|decl::SHORT))
	    set_semant_error(this, "Only floating-point types "
			     "can be '_Complex'");
	else if (base_info->spec_bits & decl::SIGNED)
	    set_semant_error(this, "Floating point types can't be "
			     "declared 'signed'");
	else if (base_info->spec_bits & decl::UNSIGNED)
	    set_semant_error(this, "Floating point types can't be 'unsigned'");
	else if (base_info->spec_bits & decl::LONG_LONG)
	    set_semant_error(this, "Only integers can be 'long long'");
	else
	    base_info->spec_bits |= decl::_COMPLEX;
    }
}

// type_spec -> _IMAGINARY 
operator ImaginaryTypeSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits & decl::_IMAGINARY)
	    set_semant_error(this, "Too many '_Imaginary's in type");
	else if (base_info->spec_bits & decl::STRUCT_UNION_ENUM)
	    set_semant_error(this, "A struct, union, or enum specifier is not"
			     " allowed with any other type specifier");
	else if (base_info->spec_bits & decl::VOID)
	    set_semant_error(this, "'void' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::_BOOL)
	    set_semant_error(this, "'_Bool' is not allowed with "
			     "any other type specifier");
	else if (base_info->spec_bits & decl::_COMPLEX)
	    set_semant_error(this, "'_Complex' and '_Imaginary' are "
			     "mutually exclusive");
	else if (base_info->spec_bits & (decl::CHAR|decl::INT|decl::SHORT))
	    set_semant_error(this, "Only floating-point types "
			     "can be '_Imaginary'");
	else if (base_info->spec_bits & decl::SIGNED)
	    set_semant_error(this, "Floating point types can't be "
			     "declared 'signed'");
	else if (base_info->spec_bits & decl::UNSIGNED)
	    set_semant_error(this, "Floating point types can't be 'unsigned'");
	else if (base_info->spec_bits & decl::LONG_LONG)
	    set_semant_error(this, "Only integers can be 'long long'");
	else
	    base_info->spec_bits |= decl::_IMAGINARY;
    }
}

// type_spec -> STRUCT OptR28E1:name LBRACE SeqR28E3star:decls RBRACE 
operator StructDefinitionSpec extends type_spec {
    public slot CompleteStructUnionType struct_type;

    public virtual method StructUnionType *get_struct_union_type() {
	return &struct_type;
    }

    public virtual method void check_types(TypecheckContext *ctx) {
	if (has_name())
	    struct_type.tag = get_name_name()->pooled_string();
	else
	    struct_type.tag = "(unnamed)";
	struct_type.is_union = false;
	for (decls_iterator i = get_decls(); i; ++i)
	    i.get_decl()->check_member(ctx, &struct_type);
    }
    
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits)
	    set_semant_error(this, "A struct specifier is not allowed "
			     "with any other type specifier");
	else {
	    base_info->spec_bits |= decl::STRUCT_UNION_ENUM;
	    assert(base_info->aggregate == decl::NO_AGGREGATE);
	    base_info->aggregate = decl::AGGREGATE_DEF;
	    base_info->specified_type = &struct_type;
	}
    }
}

// type_spec -> UNION OptR28E1:name LBRACE SeqR28E3star:decls RBRACE 
operator UnionDefinitionSpec extends type_spec {
    public slot CompleteStructUnionType union_type;

    public virtual method StructUnionType *get_struct_union_type() {
	return &union_type;
    }

    public virtual method void check_types(TypecheckContext *ctx) {
	if (has_name())
	    union_type.tag = get_name_name()->pooled_string();
	else
	    union_type.tag = "(unnamed)";
	union_type.is_union = true;
	for (decls_iterator i = get_decls(); i; ++i)
	    i.get_decl()->check_member(ctx, &union_type);
    }

    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits)
	    set_semant_error(this, "A union specifier is not allowed "
			     "with any other type specifier");
	else {
	    base_info->spec_bits |= decl::STRUCT_UNION_ENUM;
	    assert(base_info->aggregate == decl::NO_AGGREGATE);
	    base_info->aggregate = decl::AGGREGATE_DEF;
	    base_info->specified_type = &union_type;
	}
    }
}

// type_spec -> STRUCT IDSYM:name 
operator StructReferenceSpec extends type_spec {
    public slot StructUnionType *struct_type;

    public virtual method StructUnionType *get_struct_union_type() {
	return struct_type;
    }

    public virtual method void check_types(TypecheckContext *ctx) {
	Node *def = (Node *)definition.value();
	if (def)
	    struct_type = ((StructDefinitionSpec*)def)
		->get_struct_union_type();
	else {
	    CompleteStructUnionType *complete_type = 0;
	    if (completion.value()) {
		complete_type = (CompleteStructUnionType *)
                   (((StructDefinitionSpec *)completion.value())
                      ->get_struct_union_type());
	    }
	    struct_type = new
		IncompleteStructUnionType(get_name()->pooled_string(), false,
	                                  complete_type);
	}
    }

    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits)
	    set_semant_error(this, "A struct specifier is not allowed "
			     "with any other type specifier");
	else {
	    base_info->spec_bits |= decl::STRUCT_UNION_ENUM;
	    assert(base_info->aggregate == decl::NO_AGGREGATE);
	    base_info->aggregate = decl::AGGREGATE_REF;
	    base_info->specified_type = struct_type;
	}
    }
}

// type_spec -> UNION IDSYM:name 
operator UnionReferenceSpec extends type_spec {
    public slot StructUnionType *union_type;

    public virtual method StructUnionType *get_struct_union_type() {
	return union_type;
    }

    public virtual method void check_types(TypecheckContext *ctx) {
	Node *def = (Node *)definition.value();
	if (def)
	    union_type = ((StructDefinitionSpec*)def)
		->get_struct_union_type();
	else {
	    CompleteStructUnionType *complete_type = 0;
	    if (completion.value()) {
		complete_type = (CompleteStructUnionType *)
                   (((UnionDefinitionSpec *)completion.value())
                      ->get_struct_union_type());
	    }
	    union_type = new
		IncompleteStructUnionType(get_name()->pooled_string(), true,
	                                  complete_type);
	}
    }

    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits)
	    set_semant_error(this, "A union specifier is not allowed "
			     "with any other type specifier");
	else {
	    base_info->spec_bits |= decl::STRUCT_UNION_ENUM;
	    assert(base_info->aggregate == decl::NO_AGGREGATE);
	    base_info->aggregate = decl::AGGREGATE_REF;
	    base_info->specified_type = union_type;
	}
    }
}

// type_spec -> ENUM OptR28E1:name LBRACE SeqR32E3star:enums OptR32E4 RBRACE 
operator DefinedEnumSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits)
	    set_semant_error(this, "An enum specifier is not allowed "
			     "with any other type specifier");
	else {
	    base_info->spec_bits |= decl::STRUCT_UNION_ENUM;
	    assert(base_info->aggregate == decl::NO_AGGREGATE);
	    base_info->aggregate = decl::AGGREGATE_DEF;
	    base_info->specified_type = &ctx->the_SignedIntType;
	}
    }
}

// type_spec -> ENUM IDSYM:name 
operator ReferencedEnumSpec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits)
	    set_semant_error(this, "An enum specifier is not allowed "
			     "with any other type specifier");
	else {
	    base_info->spec_bits |= decl::STRUCT_UNION_ENUM;
	    assert(base_info->aggregate == decl::NO_AGGREGATE);
	    base_info->aggregate = decl::AGGREGATE_REF;
	    base_info->specified_type = &ctx->the_SignedIntType;
	}
    }
}

// type_spec -> IDSYM:type_name 
operator TypedefTypeSpec extends type_spec depends on Declarator {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	if (base_info->spec_bits & decl::TYPEDEF_NAME)
	    base_info->spec_bits |= decl::TYPEDEF_TYPEDEF;
	else
	    base_info->spec_bits |= decl::TYPEDEF_NAME;	
	Declarator *def = (Declarator *)definition.value();
	if (def)
	    base_info->specified_type = def->c_type;
    }
}

operator no_type_spec extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
    }
}

operator type_spec_sym extends type_spec {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	type_spec::phylum_cast(primary_alternative())->specify(ctx, base_info);
    }
    public virtual method void check_types(TypecheckContext *ctx) {
	type_spec::phylum_cast(primary_alternative())->check_types(ctx);
    }
}

phylum type_qual {
    public virtual method void specify(decl::TypeInfo *base_info) {}
}

// type_qual -> CONST 
operator ConstTypeQualifier extends type_qual {
    public virtual method void specify(decl::TypeInfo *base_info) {
	base_info->quals |= CONST_QUAL;
    }
}

// type_qual -> VOLATILE 
operator VolatileTypeQualifier extends type_qual {
    public virtual method void specify(decl::TypeInfo *base_info) {
	base_info->quals |= VOLATILE_QUAL;
    }
}

// type_qual -> RESTRICT 
operator RestrictTypeQualifier extends type_qual {
    public virtual method void specify(decl::TypeInfo *base_info) {
	base_info->quals |= RESTRICT_QUAL;
    }
}

operator no_type_qual extends type_qual {
}

operator type_qual_sym extends type_qual {
    public virtual method void specify(decl::TypeInfo *base_info) {
	type_qual::phylum_cast(primary_alternative())->specify(base_info);
    }
}

phylum init_declr {
    public virtual method void check_types(TypecheckContext *ctx,
					   CType *base_type) {
    }
}

// init_declr -> declr:declr OptR38E1:init 
operator InitDeclr extends init_declr {
    public virtual method void check_types(TypecheckContext *ctx,
					   CType *base_type) {
	CType *type = get_declr()->build_type(ctx, base_type);
	if (has_init())
	    get_init_init()->check_type(ctx, type);
    }
}

operator no_init_declr extends init_declr {
}

operator init_declr_sym extends init_declr {
    public virtual method void check_types(TypecheckContext *ctx,
					   CType *base_type) {
	init_declr::phylum_cast(primary_alternative())
	    ->check_types(ctx, base_type);
    }
}

phylum type_name {
    public virtual method CType *get_type(TypecheckContext *ctx) {
	return &ctx->the_UnimplementedType;
    }
}

// type_name -> SeqR13E0star:spec_quals OptR13E1:declr 
operator TypeName extends type_name depends on decl {
    public virtual method CType *get_type(TypecheckContext *ctx) {
	decl::TypeInfo base_info;
	for (spec_quals_iterator i = get_spec_quals(); i; ++i)
	    i.get_spec_qual()->specify(ctx, &base_info);
	CType *base_type = decl::assemble_base_type(ctx, this, &base_info);
	if (has_declr())
	    return get_declr_declr()->build_type(ctx, base_type);
	else
	    return base_type;
    }
}

operator no_type_name extends type_name {
}

operator type_name_sym extends type_name {
    public virtual method CType *get_type(TypecheckContext *ctx) {
	return type_name::phylum_cast(primary_alternative())->get_type(ctx);
    }
}

phylum type_spec_qual {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {}
    public virtual method void check_types(TypecheckContext *ctx) {}
}

// type_spec_qual -> type_spec:spec 
operator TypeSpecifier extends type_spec_qual {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	get_spec()->specify(ctx, base_info);
    }
    public virtual method void check_types(TypecheckContext *ctx) {
	get_spec()->check_types(ctx);
    }
}

// type_spec_qual -> type_qual:qual 
operator TypeQualifier extends type_spec_qual {
}

operator no_type_spec_qual extends type_spec_qual {
}

operator type_spec_qual_sym extends type_spec_qual {
    public virtual method void specify(TypecheckContext *ctx,
				       decl::TypeInfo *base_info) {
	type_spec_qual::phylum_cast(primary_alternative())
	    ->specify(ctx, base_info);
    }
    public virtual method void check_types(TypecheckContext *ctx) {
	type_spec_qual::phylum_cast(primary_alternative())
	    ->check_types(ctx);
    }
}

phylum abst_declr {
    public slot CType *c_type;

    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	return base_type;
    }
}

// abst_declr -> pointer:ptr 
operator PointerAbstDecl extends abst_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	CType *type = base_type;
	type = get_ptr()->point_to(ctx, type);
	c_type = type;
	return type;
    }
}

// abst_declr -> OptR46E0:ptr direct_abst_declr:direct 
operator DirectAbstDecl extends abst_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	CType *type = base_type;
	if (has_ptr())
	    type = get_ptr_ptr()->point_to(ctx, type);
	type = get_direct()->build_type(ctx, type);
	c_type = type;
	return type;
    }
}

operator no_abst_declr extends abst_declr {
}

operator abst_declr_sym extends abst_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	return abst_declr::phylum_cast(primary_alternative())
	    ->build_type(ctx, base_type);
    }
}

phylum struct_decl {
    public virtual method void check_member(TypecheckContext *ctx,
					    CompleteStructUnionType *members) {	
    }
}

// struct_decl -> SeqR13E0star:spec_quals SeqR61E1star:declrs SEMI 
operator StructDecl extends struct_decl {
    public virtual method void check_member(TypecheckContext *ctx,
					    CompleteStructUnionType *members) {
	decl::TypeInfo base_type_info;
	for (spec_quals_iterator i = get_spec_quals(); i; ++i)
	    i.get_spec_qual()->check_types(ctx);
	for (spec_quals_iterator i = get_spec_quals(); i; ++i)
	    i.get_spec_qual()->specify(ctx, &base_type_info);
	CType *base_type =
	    decl::assemble_base_type(ctx, this, &base_type_info);
	//string msg = "Base type: " + base_type->name();
	//set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	//compute_synth_attrs(true);
	bool has_declrs = get_declrs();
	if (base_type_info.aggregate == decl::NO_AGGREGATE && !has_declrs)
	    set_semant_error(this, "Declaration declares neither an aggregate"
			     " nor any declarators");	    
	for (declrs_iterator i = get_declrs(); i; ++i)
	    i.get_declr()->check_member(ctx, members, base_type);
    }
}

operator no_struct_decl extends struct_decl {
}

operator struct_decl_sym extends struct_decl {
    public virtual method void check_member(TypecheckContext *ctx,
					    CompleteStructUnionType *members) {
	struct_decl::phylum_cast(primary_alternative())
	    ->check_member(ctx, members);
    }
}

phylum enum_ {
}

// enum_ -> IDSYM:name OptR45E1:init 
operator EnumerationItem extends enum_ {
}

operator no_enum_ extends enum_ {
}

operator enum__sym extends enum_ {
}

phylum init_ {
    public virtual method void check_type(TypecheckContext *ctx,
					  CType *declared_type) {
    }
}

// init_ -> assign_expr:expr 
operator ExprInit extends init_ {
    public virtual method void check_type(TypecheckContext *ctx,
					  CType *declared_type) {
	ExprType analyzed_type = get_expr()->determine_type(ctx);
	if (!analyzed_type)
	    return;
	analyzed_type = analyzed_type.to_rvalue();
	if (!analyzed_type)
	    set_semant_error(this, "Can't initialize with non-rvalue %s",
	                     analyzed_type.name().c_str());
	else
	    // This will set its own error messages
	    ExprType::assign_compat(declared_type->unqualified(),
	                            analyzed_type, "initialization", this);
    }
}

// init_ -> LBRACE SeqR44E1star:inits OptR32E4 RBRACE 
operator BlockInit extends init_ {
}

operator no_init_ extends init_ {
}

operator init__sym extends init_ {
    public virtual method void check_type(TypecheckContext *ctx,
					  CType *declared_type) {
	init_::phylum_cast(primary_alternative())
	    ->check_type(ctx, declared_type);
    }
}

phylum designator {
}

// designator -> LBRACK expr:idx RBRACK 
operator ArrayElemDesignator extends designator {
}

// designator -> DOT IDSYM:field 
operator FieldDesignator extends designator {
}

operator no_designator extends designator {
}

operator designator_sym extends designator {
}

phylum expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return ExprType(ERROR_CLASS, 0);
    }

    public virtual method bool int_const_expr(TypecheckContext *ctx,
					      bignum_t *value) {
	return false;
    }

    public virtual method bool has_addr(TypecheckContext *ctx) {
	return false;
    }

    public virtual method ExprType determine_type_logical(
				   TypecheckContext *ctx, Node *node,
				   expr *left_expr, expr *right_expr,
				   const char *op_sym, const char *op_name) {
	ExprType left, right, tmp;
	if (!(left = left_expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if (!(right = right_expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = left.to_rvalue())) {
	    left = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as left arg to `%s'",
			     left.name().c_str(), op_sym);
	}
	if ((tmp = right.to_rvalue())) {
	    right = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as right arg to `%s'",
			     right.name().c_str(), op_sym);
	}
	if (!left.ty->is_scalar()) {
	    set_semant_error(node, "Left arg of `%s' must have scalar type,"
			     " not %s", op_sym, left.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	if (!right.ty->is_scalar()) {
	    set_semant_error(node, "Right arg of `%s' must have scalar type,"
			     " not %s", op_sym, right.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	ExpressionClass result_cl;
	ExprType result;
	if (left.is_int_const() && right.is_int_const())
	    result.cl = INTEGER_CONST;
	else if (left.is_arith_const() && right.is_arith_const())
	    result.cl = ARITH_CONST;
	else
	    result.cl = RVALUE;
       
	result.ty = &ctx->the_SignedIntType;
	string msg = string(op_name) + " has type " + result.ty->name();
	node->set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	node->compute_synth_attrs(true);
	return result;
    }

    public virtual method ExprType determine_type_integer(
				   TypecheckContext *ctx, Node *node,
				   expr *left_expr, expr *right_expr,
				   const char *op_sym, const char *op_name) {
	ExprType left, right, tmp;
	if (!(left = left_expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if (!(right = right_expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = left.to_rvalue())) {
	    left = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as left arg to `%s'",
			     left.name().c_str(), op_sym);
	}
	if ((tmp = right.to_rvalue())) {
	    right = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as right arg to `%s'",
			     right.name().c_str(), op_sym);
	}
	IntegerType *left_int, *right_int;
	if (left.ty->is_integer())
	    left_int = (IntegerType *)left.ty;
	else {
	    set_semant_error(node, "Left arg of `%s' must have integer type,"
			     " not %s", op_sym, left.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	if (right.ty->is_integer())
	    right_int = (IntegerType *)right.ty;
	else {
	    set_semant_error(node, "Right arg of `%s' must have integer type,"
			     " not %s", op_sym, right.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	ExpressionClass result_cl;
	ExprType result;
	if (left.is_int_const() && right.is_int_const())
	    result.cl = INTEGER_CONST;
	else if (left.is_arith_const() && right.is_arith_const())
	    result.cl = ARITH_CONST;
	else
	    result.cl = RVALUE;
       
	result.ty = IntegerType::usual_conv(left_int, right_int);
	string msg = string(op_name) + " has type " + result.ty->name();
	node->set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	node->compute_synth_attrs(true);
	return result;
    }

    public static method ExprType determine_type_equality(
                                  TypecheckContext *ctx, Node *node,
				  expr *left_expr, expr *right_expr,
				  const char *op_sym, const char *op_name) {
	ExprType left, right, tmp;
	if (!(left = left_expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if (!(right = right_expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = left.to_rvalue())) {
	    left = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as left arg to `%s'",
			     left.name().c_str(), op_sym);
	}
	if ((tmp = right.to_rvalue())) {
	    right = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as right arg to `%s'",
			     right.name().c_str(), op_sym);
	}
	ExprType result;
	if (left.ty->is_arithmetic() && right.ty->is_arithmetic()) {
	    ArithmeticType *left_a = (ArithmeticType*)left.ty;
	    ArithmeticType *right_a = (ArithmeticType*)right.ty;
	    CType *common = 
		ArithmeticType::usual_conv(&left_a, &right_a);
	    if (left.is_int_const() && right.is_int_const())
		result.cl = INTEGER_CONST;
	    else if (left.is_arith_const() && right.is_arith_const())
		result.cl = ARITH_CONST;
	    else
		result.cl = RVALUE;
	} else if (left.ty->is_pointer() && right.ty->is_pointer()) {
	    CType *left_ref_uq = ((PointerType*)left.ty)->referent_type
		->unqualified();
	    CType *right_ref_uq = ((PointerType*)right.ty)->referent_type
		->unqualified();
	    if (CType::composite_type(left_ref_uq, right_ref_uq)) {
		// Pointers to possibly qualified compatible types -- okay
	    } else if (left_ref_uq->is_void() || right_ref_uq->is_void()) {
		CType *non_void = (left_ref_uq->is_void()) ?
		    right_ref_uq : left_ref_uq;
		if (non_void->is_function())
		    set_semant_error(node, "Can't compare function pointer "
				     "and void pointer in %s %s %s",
				     left.ty->name().c_str(), op_sym,
				     right.ty->name().c_str());
		// Otherwise, you can compare void to anything
	    } else {
		set_semant_error(node, "Incompatible pointer types in "
				 "comparison %s %s %s",
				 left.ty->name().c_str(), op_sym, 
				 right.ty->name().c_str());
	    }
	    result.cl = RVALUE;
	} else if (left.ty->is_pointer() && right.is_null_const() ||
		   right.ty->is_pointer() && left.is_null_const()) {
	    // Any pointer can be compared to null
	    result.cl = RVALUE;
	} else {
	    set_semant_error(node, "Wrong types to `%s': %s %s %s",
			     op_sym, left.ty->name().c_str(),
			     op_sym, right.ty->name().c_str());
	    result.cl = RVALUE;
	}
	result.ty = &ctx->the_SignedIntType;
	string msg = string(op_name) + " has type " + result.ty->name();
	node->set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	node->compute_synth_attrs(true);
	return result;
    }

    public static method ExprType determine_type_relational(
                                  TypecheckContext *ctx, Node *node,
				  expr *left_expr, expr *right_expr,
				  const char *op_sym, const char *op_name) {
	ExprType left, right, tmp;
	if (!(left = left_expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if (!(right = right_expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = left.to_rvalue())) {
	    left = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as left arg to `%s'",
			     left.name().c_str(), op_sym);
	}
	if ((tmp = right.to_rvalue())) {
	    right = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as right arg to `%s'",
			     right.name().c_str(), op_sym);
	}
	ExprType result;
	if (left.ty->is_arithmetic() && right.ty->is_arithmetic()) {
	    ArithmeticType *left_a = (ArithmeticType*)left.ty;
	    ArithmeticType *right_a = (ArithmeticType*)right.ty;
	    if (left_a->domain() != REAL_DOMAIN)
		set_semant_error(node, "Left arg to `%s' must have real type,"
				 " not %s", op_sym, left_a->name().c_str());
	    if (right_a->domain() != REAL_DOMAIN)
		set_semant_error(node, "Right arg to `%s' must have real type,"
				 " not %s", op_sym, right_a->name().c_str());
	    CType *common = 
		ArithmeticType::usual_conv(&left_a, &right_a);
	    if (left.is_int_const() && right.is_int_const())
		result.cl = INTEGER_CONST;
	    else if (left.is_arith_const() && right.is_arith_const())
		result.cl = ARITH_CONST;
	    else
		result.cl = RVALUE;
	} else if (left.ty->is_pointer() && right.ty->is_pointer()) {
	    CType *left_ref_uq = ((PointerType*)left.ty)->referent_type
		->unqualified();
	    CType *right_ref_uq = ((PointerType*)right.ty)->referent_type
		->unqualified();
	    if (left_ref_uq->is_function() || right_ref_uq->is_function())
		set_semant_error(node, "Can't compare function pointers for "
				 "order in %s %s %s", left.ty->name().c_str(),
				 op_sym, right.ty->name().c_str());
	    else if (left_ref_uq->is_complete() != right_ref_uq->is_complete())
		set_semant_error(node, "Can't compare complete and incomplete"
				 " types in %s %s %s", left.ty->name().c_str(),
				 op_sym, right.ty->name().c_str());
	    else if (!CType::composite_type(left_ref_uq, right_ref_uq))
		set_semant_error(node, "Incompatible types in comparison "
				 "%s %s %s", left.ty->name().c_str(),
				 op_sym, right.ty->name().c_str());
	    result.cl = RVALUE;
	} else {
	    set_semant_error(node, "Wrong types to `%s': %s %s %s",
			     op_sym, left.ty->name().c_str(),
			     op_sym, right.ty->name().c_str());
	    result.cl = RVALUE;
	}
	result.ty = &ctx->the_SignedIntType;
	string msg = string(op_name) + " has type " + result.ty->name();
	node->set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	node->compute_synth_attrs(true);
	return result;
    }

    public virtual method ExprType determine_type_shift(
				   TypecheckContext *ctx, Node *node,
				   expr *left_expr, expr *right_expr,
				   const char *op_sym, const char *op_name) {
	ExprType left, right, tmp;
	if (!(left = left_expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if (!(right = right_expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = left.to_rvalue())) {
	    left = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as left arg to `%s'",
			     left.name().c_str(), op_sym);
	}
	if ((tmp = right.to_rvalue())) {
	    right = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as right arg to `%s'",
			     right.name().c_str(), op_sym);
	}
	IntegerType *left_int, *right_int;
	if (left.ty->is_integer())
	    left_int = (IntegerType *)left.ty;
	else {
	    set_semant_error(node, "Left arg of shift must have integer type,"
			     " not %s", left.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	if (right.ty->is_integer())
	    right_int = (IntegerType *)right.ty;
	else {
	    set_semant_error(node, "Shift amount must have integer type,"
			     " not %s", right.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	ExpressionClass result_cl;
	if (left.is_int_const() && right.is_int_const())
	    result_cl = INTEGER_CONST;
	else if (left.is_arith_const() && right.is_arith_const())
	    result_cl = ARITH_CONST;
	else
	    result_cl = RVALUE;
	IntegerType *result = left_int->promote();
	string msg = string(op_name) + " has type " + result->name();
	node->set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	node->compute_synth_attrs(true);
	return ExprType(result_cl, result);
    }

    public virtual method ExprType determine_type_arithmetic(
				   TypecheckContext *ctx, Node *node,
				   expr *left_expr, expr *right_expr,
				   const char *op_sym, const char *op_name) {
	ExprType left, right, tmp;
	if (!(left = left_expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if (!(right = right_expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = left.to_rvalue())) {
	    left = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as left arg to `%s'",
			     left.name().c_str(), op_sym);
	}
	if ((tmp = right.to_rvalue())) {
	    right = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as right arg to `%s'",
			     right.name().c_str(), op_sym);
	}
	ArithmeticType *left_a, *right_a;
	if (left.ty->is_arithmetic())
	    left_a = (ArithmeticType *)left.ty;
	else {
	    set_semant_error(node, "Left arg of `%s' must have arithemtic "
			     "type, not %s", op_sym, left.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	if (right.ty->is_arithmetic())
	    right_a = (ArithmeticType *)right.ty;
	else {
	    set_semant_error(node, "Right arg of `%s' must have arithemtic "
			     "type, not %s", op_sym, right.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	ExprType result;
	if (left.is_int_const() && right.is_int_const())
	    result.cl = INTEGER_CONST;
	else if (left.is_arith_const() && right.is_arith_const())
	    result.cl = ARITH_CONST;
	else
	    result.cl = RVALUE;
       
	result.ty = ArithmeticType::usual_conv(&left_a, &right_a);
	string msg = string(op_name) + " has type " + result.ty->name();
	node->set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	node->compute_synth_attrs(true);
	return result;
    }

    public virtual method ExprType determine_type_unary(
				   TypecheckContext *ctx, Node *node,
				   expr *expr, const char *op_sym,
				   const char *op_name) {
	ExprType type, tmp;
	if (!(type = expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = type.to_rvalue())) {
	    type = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as arg to `%s'",
			     type.name().c_str(), op_sym);
	}
	ArithmeticType *type_a;
	if (type.ty->is_arithmetic())
	    type_a = (ArithmeticType *)type.ty;
	else {
	    set_semant_error(node, "Arg of `%s' must have arithmetic "
			     "type, not %s", op_sym, type.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	ExprType result;
	if (type.is_int_const())
	    result.cl = INTEGER_CONST;
	else if (type.is_arith_const())
	    result.cl = ARITH_CONST;
	else
	    result.cl = RVALUE;
	if (type_a->is_integer())
	    type_a = ((IntegerType *)type_a)->promote();
	result.ty = type_a;
	string msg = string(op_name) + " has type " + result.ty->name();
	node->set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	node->compute_synth_attrs(true);
	return result;
    }

    public virtual method ExprType determine_type_crement(
				   TypecheckContext *ctx, Node *node,
				   expr *expr, const char *op_sym,
				   const char *op_name) {
	ExprType type, tmp;
	if (!(type = expr->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = type.to_mod_lvalue())) {
	    type = tmp;
	} else {
	    set_semant_error(node, "Can't use %s as arg to `%s'",
			     type.name().c_str(), op_sym);
	}
	if (!(type.ty->is_arithmetic() &&
	      ((ArithmeticType *)type.ty)->domain() == REAL_DOMAIN) &&
	    !type.ty->is_pointer()) {
	    set_semant_error(node, "Arg of `%s' must have pointer or real "
			     "arithemtic type, not %s", op_sym,
			     type.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	ExprType result;
	result.cl = RVALUE;
	if (type.ty->is_integer())
	    type.ty = ((IntegerType *)type.ty)->promote();
	result.ty = type.ty;
	string msg = string(op_name) + " has type " + result.ty->name();
	node->set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	node->compute_synth_attrs(true);
	return result;
    }
}

// expr -> expr:pred QUESTION Opt(exprs):texpr COLON expr:fexpr 
operator TernaryOp extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType pred, middle, right, tmp;
	if (!(pred = get_pred()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if (has_texpr()) {
	    if (!(middle = get_texpr_expr()->determine_type(ctx)))
		return ExprType(ERROR_CLASS, 0);
	} else {
	    middle = pred; /* a GCC extension */
	}
	if (!(right = get_fexpr()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = pred.to_rvalue())) {
	    pred = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as predicate in `?:'",
			     pred.name().c_str());
	}
	if ((tmp = middle.to_rvalue())) {
	    middle = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as middle arg of `?:'",
			     middle.name().c_str());
	}
	if ((tmp = right.to_rvalue())) {
	    right = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as right arg of `?:'",
			     right.name().c_str());
	}
	if (!pred.ty->is_scalar())
	    set_semant_error(this, "Predicate of ternary op must have scalar "
			     "type, not %s", pred.ty->name().c_str());
	ExprType result;
	if (middle.ty->is_arithmetic() && right.ty->is_arithmetic()) {
	    ArithmeticType *middle_converted = (ArithmeticType*)middle.ty;
	    ArithmeticType *right_converted = (ArithmeticType*)right.ty;
	    result.ty = ArithmeticType::usual_conv(&middle_converted,
						   &right_converted);
	    if (pred.is_int_const() && middle.is_int_const() &&
		right.is_int_const())
		result.cl = INTEGER_CONST;
	    else if (pred.is_int_const() && middle.is_arith_const() &&
		     right.is_arith_const())
		result.cl = ARITH_CONST;
	    else
		result.cl = RVALUE;
	} else if (middle.ty->is_struct_union() &&
		   right.ty->is_struct_union()) {
	    StructUnionType *middle_su = (StructUnionType *)middle.ty;
	    StructUnionType *right_su = (StructUnionType *)right.ty;
	    StructUnionType *common =
		StructUnionType::are_same(middle_su, right_su);
	    if (common) {
		result.ty = common;
		result.cl = RVALUE;
	    } else {
		set_semant_error(this, "Different struct/union types in `?:':"
				 " %s vs. %s", middle_su->name().c_str(),
				 right_su->name().c_str());
		return ExprType(ERROR_CLASS, 0);
	    }
	} else if (middle.ty->is_void() && right.ty->is_void()) {
	    result.cl = RVALUE;
	    result.ty = &ctx->the_VoidType;
	} else if ((middle.ty->is_pointer() || middle.is_null_const()) &&
		   (right.ty->is_pointer() || right.is_null_const())) {
	    if (!middle.ty->is_pointer()) {
		// Middle must be an arithmetic 0
		result.ty = right.ty;
	    } else if (!right.ty->is_pointer()) {
		// Right must be an arithmetic 0
		result.ty = middle.ty;
	    } else {
		CType *middle_ref = ((PointerType *)middle.ty)->referent_type;
		CType *right_ref = ((PointerType *)right.ty)->referent_type;
		unsigned quals = middle_ref->qualifiers() |
		    right_ref->qualifiers();
		CType *middle_ref_uq = middle_ref->unqualified();
		CType *right_ref_uq = right_ref->unqualified();
		CType *comp = CType::composite_type(middle_ref_uq,
						    right_ref_uq);
		if (comp) {
		    result.ty = comp->qualified_by(quals)->pointer_to();
		} else if (middle_ref_uq->is_void() ||
			   right_ref_uq->is_void()) {
		    result.ty = ctx->the_VoidType.qualified_by(quals)
			->pointer_to();
		} else {
		    set_semant_error(this, "Incompatible pointer types %s "
				     "and %s in conditional operator",
				     middle.ty->name().c_str(),
				     right.ty->name().c_str());
		    return ExprType(ERROR_CLASS, 0);
		}
	    }
	    result.cl = RVALUE;
	} else {
	    set_semant_error(this, "Incompatible types in conditional: "
			     "? %s : %s", middle.ty->name().c_str(),
			     right.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	string msg = "Conditional op has type " + result.ty->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return result;
    }
}

// expr -> expr:left LOR expr:right 
operator LogicalOr extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_logical(ctx, this, get_left(), get_right(),
					    "||", "Logical OR");
    }
}

// expr -> expr:left LAND expr:right 
operator LogicalAnd extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_logical(ctx, this, get_left(), get_right(),
					    "&&", "Logical AND");
    }
}

// expr -> expr:left BOR expr:right 
operator BitwiseOr extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_integer(ctx, this, get_left(), get_right(),
					    "|", "Bitwise OR");
    }
}

// expr -> expr:left BXOR expr:right 
operator BitwiseXor extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_integer(ctx, this, get_left(), get_right(),
					    "^", "Bitwise XOR");
    }
}

// expr -> expr:left BAND expr:right 
operator BitwiseAnd extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_integer(ctx, this, get_left(), get_right(),
					    "&", "Bitwise AND");
    }
}

// expr -> expr:left EQ expr:right 
operator Equals extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_equality(ctx, this, get_left(),
					     get_right(), "==", "Equals");
    }
}

// expr -> expr:left NE expr:right 
operator NotEquals extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_equality(ctx, this, get_left(),
					     get_right(), "!=", "Not-equals");
    }
}

// expr -> expr:left LT expr:right 
operator LessThan extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_relational(ctx, this, get_left(),
					       get_right(), "<", "Less-than");
    }
}

// expr -> expr:left GT expr:right 
operator GreaterThan extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_relational(ctx, this, get_left(),
					       get_right(), ">",
					       "Greater-than");
    }
}

// expr -> expr:left LE expr:right 
operator LessThanEquals extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_relational(ctx, this, get_left(),
					       get_right(), "<=",
					       "Less-than-or-equals");
    }
}

// expr -> expr:left GE expr:right 
operator GreaterThanEquals extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_relational(ctx, this, get_left(),
					       get_right(), ">=",
					       "Greater-than-or-equals");
    }
}

// expr -> expr:left LSHIFT expr:right 
operator LeftShift extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_shift(ctx, this, get_left(), get_right(),
					  "<<", "Left shift");
    }
}

// expr -> expr:left RSHIFT expr:right 
operator RightShift extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_shift(ctx, this, get_left(), get_right(),
					  ">>", "Right shift");
    }
}

// expr -> expr:left PLUS expr:right 
operator Add extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType left, right, tmp;
	if (!(left = get_left()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if (!(right = get_right()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = left.to_rvalue())) {
	    left = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as left arg to `+'",
			     left.name().c_str());
	}
	if ((tmp = right.to_rvalue())) {
	    right = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as right arg to `+'",
			     right.name().c_str());
	}
	ExprType result;
	if (left.ty->is_arithmetic() && right.ty->is_arithmetic()) {
	    ArithmeticType *left_converted = (ArithmeticType*)left.ty;
	    ArithmeticType *right_converted = (ArithmeticType*)right.ty;
	    result.ty = 
		ArithmeticType::usual_conv(&left_converted, &right_converted);
	    if (left.is_int_const() && right.is_int_const())
		result.cl = INTEGER_CONST;
	    else if (left.is_arith_const() && right.is_arith_const())
		result.cl = ARITH_CONST;
	    else
		result.cl = RVALUE;
	} else if (left.ty->is_integer() && right.ty->is_ptr_to_object() ||
		   right.ty->is_integer() && left.ty->is_ptr_to_object()) {
	    ExprType int_type;
	    ExprType pointer_type;
	    if (left.ty->is_integer()) {
		int_type = left;
		pointer_type = right;
	    } else {
		pointer_type = left;
		int_type = right;
	    }
	    result.ty = pointer_type.ty;
	    if (int_type.is_int_const() && pointer_type.is_addr_const())
		result.cl = CONSTANT;
	    else
		result.cl = RVALUE;
	} else {
	    set_semant_error(this, "Wrong types to `+': %s + %s",
			     left.ty->name().c_str(),
			     right.ty->name().c_str());
	    result.ty = 0;
	    result.cl = ERROR_CLASS;
	    return result;
	}
	string msg = "Sum has type " + result.ty->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return result;
    }

    public virtual method bool int_const_expr(TypecheckContext *ctx,
					      bignum_t *value) {
	bignum_t left, right;
	if (!get_left()->int_const_expr(ctx, &left))
	    return false;
	if (!get_right()->int_const_expr(ctx, &right))
	    return false;
	*value = left + right;
	return true;
    }
}

// expr -> expr:left MINUS expr:right 
operator Subtract extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType left, right, tmp;
	if (!(left = get_left()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if (!(right = get_right()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = left.to_rvalue())) {
	    left = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as left arg to `-'",
			     left.name().c_str());
	}
	if ((tmp = right.to_rvalue())) {
	    right = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as right arg to `-'",
			     right.name().c_str());
	}
	ExprType result;
	if (left.ty->is_arithmetic() && right.ty->is_arithmetic()) {
	    ArithmeticType *left_converted = (ArithmeticType*)left.ty;
	    ArithmeticType *right_converted = (ArithmeticType*)right.ty;
	    result.ty = 
		ArithmeticType::usual_conv(&left_converted, &right_converted);
	    if (left.is_int_const() && right.is_int_const())
		result.cl = INTEGER_CONST;
	    else if (left.is_arith_const() && right.is_arith_const())
		result.cl = ARITH_CONST;
	    else
		result.cl = RVALUE;
	} else if (right.ty->is_integer() && left.ty->is_ptr_to_object()) {
	    result.ty = left.ty;
	    if (right.is_int_const() && left.is_addr_const())
		result.cl = CONSTANT;
	    else
		result.cl = RVALUE;
	} else if (left.ty->is_pointer() && right.ty->is_pointer()) {
	    CType *left_ref_uq = ((PointerType *)left.ty)->referent_type
		->unqualified();
	    CType *right_ref_uq = ((PointerType *)right.ty)->referent_type
		->unqualified();
	    if (!CType::composite_type(left_ref_uq, right_ref_uq))
		set_semant_error(this, "Incompatible pointer types in "
				 "difference: %s - %s",
				 left.ty->name().c_str(),
				 right.ty->name().c_str());
	    result.ty = ctx->the_PtrdiffType;
	    result.cl = RVALUE;
	} else {
	    set_semant_error(this, "Wrong types to `-': %s - %s",
			     left.ty->name().c_str(),
			     right.ty->name().c_str());
	    result.ty = 0;
	    result.cl = ERROR_CLASS;
	    return result;
	}
	string msg = "Difference has type " + result.ty->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return result;
    }
}

// expr -> expr:left TIMES expr:right 
operator Multiply extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_arithmetic(ctx, this, get_left(),
					       get_right(), "*", "Multiply");
    }
}

// expr -> expr:left DIV expr:right 
operator Divide extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_arithmetic(ctx, this, get_left(),
					       get_right(), "/", "Divide");
    }
}

// expr -> expr:left MOD expr:right 
operator Modulo extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_integer(ctx, this, get_left(), get_right(),
					    "%", "Modulus");
    }
}

// expr -> LPAREN type_name:name RPAREN expr:expr 
operator TypeCast extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType expr_type;
	if (!(expr_type = get_expr()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	CType *type = get_name()->get_type(ctx);
	if (type->unqualified()->is_void()) {
	    return ExprType(VOID_EXPR, type);
	} else {
	    if (!expr_type.ty->is_scalar())
		set_semant_error(this, "Can't cast value of non-scalar type "
				 "%s", expr_type.ty->name().c_str());
	    if (!type->unqualified()->is_scalar())
		set_semant_error(this, "Can't cast to non-scalar type %s",
				 type->name().c_str());
	    ExprType result;
	    result.ty = type;
	    if (expr_type.is_arith_const() && type->is_integer())
		result.cl = INTEGER_CONST;
	    else if (expr_type.is_null_const() && type->is_pointer())
		result.cl = NULL_CONST;
	    else if (expr_type.is_int_const() && !type->is_integer())
		result.cl = ARITH_CONST;
	    else
		/* GCC would have "result.cl = expr_type.cl" to allow
                   lvalues */
		result = result.to_rvalue(); 
	    string msg = "Cast has type " + result.ty->name();
	    set_string_prop(MOUSE_OVER_MSG, msg.c_str());	
	    compute_synth_attrs(true);
	    return result;
	}
    }
}

// expr -> BAND expr:expr 
operator AddressOf extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType type, tmp;
	if (!(type = get_expr()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = type.to_lvalue())) {
	    type = tmp;
	} else if (type.cl == FUNC_DESIG || get_expr()->has_addr(ctx)) {
	    // okay
	} else {
	    set_semant_error(this, "Can't use %s as arg to `&'",
			     type.name().c_str());
	}
	if (type.ty->is_bitfield())
	    set_semant_error(this, "Can't take pointer to bitfield type %s",
			     type.ty->name().c_str());
	ExprType result;
	result.ty = type.ty->pointer_to();
	result.cl = RVALUE; // XXX sometimes should be ADDR_CONST
	string msg = "Address-of has type " + result.ty->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return result;
    }
}

// expr -> PLUS expr:expr 
operator Positive extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_unary(ctx, this, get_expr(), "+",
					  "Unary plus");
    }
}

// expr -> MINUS expr:expr 
operator Negate extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_unary(ctx, this, get_expr(), "-",
					  "Unary minus");
    }
}

// expr -> BNOT expr:expr 
operator BitwiseNegate extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType type, tmp;
	if (!(type = get_expr()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = type.to_rvalue())) {
	    type = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as arg to `~'",
			     type.name().c_str());
	}
	IntegerType *type_int;
	if (type.ty->is_integer())
	    type_int = (IntegerType *)type.ty;
	else {
	    set_semant_error(this, "Arg of `~' must have arithemtic "
			     "type, not %s", type.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	ExprType result;
	if (type.is_int_const())
	    result.cl = INTEGER_CONST;
	else if (type.is_arith_const())
	    result.cl = ARITH_CONST;
	else
	    result.cl = RVALUE;
	type_int = type_int->promote();
	result.ty = type_int;
	string msg = "Bitwise complement has type " + result.ty->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return result;
    }
}

// expr -> NOT expr:expr 
operator LogicalNot extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType type, tmp;
	if (!(type = get_expr()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = type.to_rvalue())) {
	    type = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as arg to `!'",
			     type.name().c_str());
	}
	if (!type.ty->is_scalar()) {
	    set_semant_error(this, "Arg of `!' must have scalar "
			     "type, not %s", type.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	ExprType result;
	if (type.is_int_const())
	    result.cl = INTEGER_CONST;
	else if (type.is_const())
	    result.cl = ARITH_CONST;
	else
	    result.cl = RVALUE;
	result.ty = &ctx->the_SignedIntType;
	string msg = "Logical not has type " + result.ty->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return result;
    }
}

// expr -> expr:func LPAREN SeqR127E2star:exprs RPAREN 
operator FunctionCall extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType type, tmp;
	if (!(type = get_func()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = type.to_rvalue())) {
	    type = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as function in function call",
			     type.name().c_str());
	}
	FunctionType *func_type = NULL;
	if (type.ty->is_pointer()) {
	    PointerType *ptr_type = (PointerType *)type.ty;
	    CType *ref_t = ptr_type->referent_type->unqualified();
	    if (ref_t->is_function())
		func_type = (FunctionType *)ref_t;
	}
	if (!func_type) {
	    set_semant_error(this, "Function in function call must have "
			     "pointer to function type, not %s",
			     type.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	int proto_args = 0;
	if (!(func_type->flags & UNKNOWN_ARGS))
	    proto_args = (int)func_type->parameters.size(); 
	int argnum = 0;
	for (exprs_iterator i = get_exprs(); i; ++i, argnum++) {
	    ExprType tmp, arg_type = i.get_expr()->determine_type(ctx);
	    if (!arg_type)
		continue;
	    if (!(tmp = arg_type.to_rvalue())) {
		set_semant_error(i.get_expr()->cast_to_Node(),
				 "Can't use %s as argument in function call",
				 arg_type.name().c_str());
	    } else {
		arg_type = tmp;
	    }
	    if (argnum < proto_args) {
		CType *param_type = func_type->parameters[argnum];
		ExprType::assign_compat(param_type, arg_type,
					"function call",
					i.get_expr()->cast_to_Node());
	    } else if (!(func_type->flags & (UNKNOWN_ARGS|ELLIPTICAL))) {
		set_semant_error(this, "Too many args (%d vs. %d) in "
				 "call to %s", argnum + 1, proto_args,
				 func_type->name().c_str());
	    } else {
		CType *promoted = arg_type.ty->arg_promote();
		// Can we do anythin with this? I think the rules about
		// argument promotion can only be enforced dynamically
	    }
	}
	if (argnum < proto_args) {
	    set_semant_error(this, "Too few args (%d vs. %d) in call to %s",
			     argnum, proto_args,
			     func_type->name().c_str());
	}
	ExprType result;
	result.ty = func_type->return_type;
	if (result.ty->is_void())
	    result.cl = VOID_EXPR;
	else
	    result.cl = RVALUE;
	string msg = "Function call has type " + result.ty->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return result;	
    }
}

// expr -> INC expr:expr 
operator PreIncrement extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_crement(ctx, this, get_expr(), "++",
					    "Prefix increment");
    }
}

// expr -> DEC expr:expr 
operator PreDecrement extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_crement(ctx, this, get_expr(), "--",
					    "Prefix decrement");
    }
}

// expr -> expr:expr INC 
operator PostIncrement extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_crement(ctx, this, get_expr(), "++",
					    "Postfix increment");
    }
}

// expr -> expr:expr DEC 
operator PostDecrement extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::determine_type_crement(ctx, this, get_expr(), "--",
					    "Postfix decrement");
    }
}

// expr -> SIZEOF expr:expr 
operator VarSizeof extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType expr_type = get_expr()->determine_type(ctx);
	CType *type = expr_type.ty;
	ExprType result;
	if (type && type->is_array() &&
	    ((ArrayType *)type)->qualifiers & VARIABLE_LEN_QUAL)
	    result.cl = RVALUE; /* C99 runtime sizeof! */
	else
	    result.cl = INTEGER_CONST;
	result.ty = ctx->the_SizeType;
	return result;
    }
}

// expr -> SIZEOF LPAREN type_name:type RPAREN 
operator TypeSizeof extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	CType *type = get_type()->get_type(ctx);
	ExprType result;
	if (type->is_array() &&
	    ((ArrayType *)type)->qualifiers & VARIABLE_LEN_QUAL)
	    result.cl = RVALUE; /* C99 runtime sizeof! */
	else
	    result.cl = INTEGER_CONST;
	result.ty = ctx->the_SizeType;
	return result;
    }
}

// expr -> LPAREN exprs:expr RPAREN 
operator ParenthExpr extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return get_expr()->determine_type(ctx);
    }
    public virtual method bool has_addr(TypecheckContext *ctx) {
	return get_expr()->has_addr(ctx);
    }
}

// expr -> int_const:val 
operator IntegerConstant extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType type;
	type.ty = get_val()->determine_type(ctx);
	bool unused;
	bignum_t value = get_val()->get_value(ctx, &unused);
	if (value)
	    type.cl = INTEGER_CONST;
	else
	    type.cl = NULL_CONST;
	string msg = "Integer constant has type " + type.name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return type;
    }
    public virtual method bool int_const_expr(TypecheckContext *ctx,
					      bignum_t *value) {
	bool unused;
	*value = get_val()->get_value(ctx, &unused);
	return true;
    }
}

// expr -> char_const:val 
operator CharacterConstant extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	CType *type = get_val()->determine_type(ctx);
	string msg = "Character constant has type " + type->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return ExprType(INTEGER_CONST, type);
    }
}

// expr -> float_const:val 
operator FloatingConstant extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	CType *type = get_val()->determine_type(ctx);
	string msg = "Floating point constant has type " + type->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return ExprType(ARITH_CONST, type);
    }
}

// expr -> str_const:val 
operator StringConstant extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	CType *type = get_val()->determine_type(ctx);
	string msg = "String constant has type " + type->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return ExprType(ARRAY_LVALUE, type);
    }
}

// expr -> LPAREN type_name:type RPAREN LBRACE SeqR44E1star:inits OptR32E4 RBRACE 
operator CompoundLiteral extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	CType *type = get_type()->get_type(ctx);
	if (type->is_function() || !type->is_complete() && !type->is_array()) {
	    set_semant_error(this, "Compound literal must specify object or "
			     "incomplete array type, not %s",
			     type->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	} else {
	    if (type->is_array()) {
		ArrayType *array_type = (ArrayType *)type;
		if (array_type->qualifiers & VARIABLE_LEN_QUAL)
		    set_semant_error(this, "Compound literal may not have "
				     "variable length array type %s",
				     array_type->name().c_str());
	    }
	    ExprType result;
	    result.ty = type;
	    if (result.ty->is_array())
		result.cl = ARRAY_LVALUE;
	    else
		result.cl = ACCESS_LVALUE;
	    string msg = "Compound literal has type " + result.ty->name();
	    set_string_prop(MOUSE_OVER_MSG, msg.c_str());	
	    compute_synth_attrs(true);
	    return result;
	}
    }
}

// expr -> IDSYM:name 
operator VariableExpr extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	Declarator *def = (Declarator *)declarator.value();
	if (def && def->c_type) {
	    CType *type = def->c_type;
	    string msg = "`" + string(get_name()->pooled_string().chars()) +
		"' has type " + type->name();
	    set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	    compute_synth_attrs(true);
	    if (type->is_function())
		return ExprType(FUNC_DESIG, type);
	    else if (type->is_array())
		return ExprType(ARRAY_LVALUE, type);
	    else
		return ExprType(MOD_LVALUE, type);
	} else {
	    return ExprType(ERROR_CLASS, 0);
	}
    }
}

// expr -> expr:expr DOT IDSYM:field 
operator StructAccess extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType orig_arg, arg;
	if (!(orig_arg = get_expr()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if (!(arg = orig_arg.to_rvalue())) {
	    set_semant_error(this, "Can't use %s as argument to . operator",
			     arg.name().c_str());
	}
	if (arg.ty->unqualified()->is_pointer()) {
	    CType *ref = ((PointerType *)arg.ty->unqualified())->referent_type;
	    if (ref->unqualified()->is_struct_union()) {
		bool union_p = ((StructUnionType*)ref->unqualified())
		    ->is_union;
		set_semant_error(this, "`.' on pointer to %s should be `->'",
			     union_p ? "union" : "structure");
		return ExprType(ERROR_CLASS, 0);
	    }
	} else if (!arg.ty->unqualified()->is_struct_union()) {
	    set_semant_error(this, "Left arg to '.' must have structure or"
			     " union type, not %s", arg.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	} else if (!arg.ty->unqualified()->completion()->is_complete()) {
	    set_semant_error(this, "Can't dereference incomplete %s type %s",
			     ((StructUnionType *)arg.ty)->is_union
			     ? "union" : "struct", arg.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	unsigned quals = arg.ty->qualifiers();
	CompleteStructUnionType *obj = (CompleteStructUnionType *)
					(arg.ty->unqualified()->completion());
	PooledString field = get_field()->pooled_string();
	CType *val = obj->table.resolve(field);
	if (!val) {
	    set_semant_error(this, "%s has no member %s",
			     obj->name().c_str(), field.chars());
	    return ExprType(ERROR_CLASS, 0);
	}
	ExprType result;
	if (val->is_array())
	    result.cl = orig_arg.is_lvalue() ? ARRAY_LVALUE : ARRAY_RVALUE;
	else if (orig_arg.is_lvalue()) {
	    if (quals & CONST_QUAL || val->qualifiers() & CONST_QUAL)
		result.cl = ACCESS_LVALUE;
	    else
		result.cl = MOD_LVALUE;
	} else
	    result.cl = RVALUE;
	result.ty = val->qualified_by(quals);
	string msg = string(obj->is_union ? "Union" : "Structure") +
	    " member has type " + result.ty->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());	
	compute_synth_attrs(true);
	return result;
    }
}

// expr -> expr:expr PTR IDSYM:field 
operator PointerAccess extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType arg, orig_arg;
	if (!(orig_arg = get_expr()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if (!(arg = orig_arg.to_rvalue())) {
	    set_semant_error(this, "Can't use %s as argument to -> operator",
			     arg.name().c_str());
	}
	if (arg.ty->unqualified()->is_struct_union()) {
	    bool union_p = ((StructUnionType *)arg.ty->unqualified())
		->is_union;
	    set_semant_error(this, "`->' on %s should be `.'",
			     union_p ? "union" : "structure");
	    return ExprType(ERROR_CLASS, 0);
	} else if (!arg.ty->unqualified()->is_pointer()) {
	    set_semant_error(this, "Left arg to '->' must have pointer to"
			     " structure or union type, not %s",
			     arg.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	CType *ref = ((PointerType *)arg.ty->unqualified())->referent_type;
	unsigned quals = ref->qualifiers();	
	CType *ref_uq = ref->unqualified();
	if (!ref_uq->is_struct_union()) {
	    set_semant_error(this, "Left arg to '->' must be pointer to"
			     " structure or union, not to %s",
			     ref->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	} else if (!ref_uq->completion()->is_complete()) {
	    set_semant_error(this, "Can't dereference incomplete %s type %s",
			     ((StructUnionType *)ref_uq)->is_union
			     ? "union" : "struct", ref->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	CompleteStructUnionType *obj
            = (CompleteStructUnionType *)(ref_uq->completion());
	PooledString field = get_field()->pooled_string();
	CType *val = obj->table.resolve(field);
	if (!val) {
	    set_semant_error(this, "%s has no member %s",
			     obj->name().c_str(), field.chars());
	    return ExprType(ERROR_CLASS, 0);
	}
	ExprType result;
	if (val->is_array())
	    result.cl = orig_arg.is_lvalue() ? ARRAY_LVALUE : ARRAY_RVALUE;
	else if (orig_arg.is_lvalue()) {
	    if (quals & CONST_QUAL || val->qualifiers() & CONST_QUAL)
		result.cl = ACCESS_LVALUE;
	    else
		result.cl = MOD_LVALUE;
	} else
	    result.cl = RVALUE;
	result.ty = val->qualified_by(quals);
	string msg = "Indirect " +
	    string(obj->is_union ? "union" : "structure") +
	    " member has type " + result.ty->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());	
	compute_synth_attrs(true);
	return result;
    }
}

// expr -> expr:expr LBRACK exprs:index RBRACK 
operator ArrayAccess extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType outside, inside, tmp;
	if (!(outside = get_expr()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = outside.to_rvalue())) {
	    outside = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as object of subscript"
			     " operator",
			     outside.name().c_str());
	}
	if (!(inside = get_index()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = inside.to_rvalue())) {
	    inside = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as inside of subscript"
			     " operator",
			     inside.name().c_str());
	}
	if (inside.ty->is_pointer() + outside.ty->is_pointer() != 1) {
	    set_semant_error(this, "Exactly one arg to array subscript "
			     "operator must be a pointer in %s[%s]",
			     outside.ty->name().c_str(),
			     inside.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	PointerType *ptr_type;
	CType *other_type;
	if (outside.ty->is_pointer()) {
	    ptr_type = (PointerType*)outside.ty;
	    other_type = inside.ty;
	} else {
	    ptr_type = (PointerType*)inside.ty;
	    other_type = outside.ty;
	}
	if (!other_type->is_integer()) {
	    set_semant_error(this, "Array index must have integer type, not "
			     "%s", other_type->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	IntegerType *idx_type = (IntegerType *)other_type;
	ExprType result;
	result.ty = ptr_type->referent_type;
	if (result.ty->is_array())
	    result.cl = ARRAY_LVALUE;
	else if (result.ty->qualifiers() & CONST_QUAL)
	    result.cl = ACCESS_LVALUE;
	else
	    result.cl = MOD_LVALUE;
	string msg = "Array index has type " + result.ty->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return result;	
    }
    public virtual method bool has_addr(TypecheckContext *ctx) {
	return true;
    }
}

// expr -> TIMES expr:expr 
operator PointerDereference extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType type, tmp;
	if (!(type = get_expr()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = type.to_rvalue())) {
	    type = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as arg to `*'",
			     type.name().c_str());
	}
	PointerType *type_p;
	if (type.ty->is_pointer())
	    type_p = (PointerType *)type.ty;
	else {
	    set_semant_error(this, "Arg of `*' must have pointer "
			     "type, not %s", type.ty->name().c_str());
	    return ExprType(ERROR_CLASS, 0);
	}
	CType *referent = type_p->referent_type;
	ExprType result;
	if (referent->unqualified()->is_function())
	    result.cl = FUNC_DESIG;
	else if (referent->unqualified()->is_object()) {
	    if (referent->qualifiers() & CONST_QUAL)
		result.cl = ACCESS_LVALUE;
	    else
		result.cl = MOD_LVALUE;
	} else if (referent->unqualified()->is_void())
	    result.cl = VOID_EXPR;
	else
	    result.cl = RVALUE;
	result.ty = referent;
	string msg = "Pointer dereference has type " + result.ty->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return result;
    }
    public virtual method bool has_addr(TypecheckContext *ctx) {
	return true;
    }
}

operator no_expr extends expr {
}

operator expr_sym extends expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return expr::phylum_cast(primary_alternative())
	    ->determine_type(ctx);
    }

    public virtual method bool int_const_expr(TypecheckContext *ctx,
					      bignum_t *value) {
	return expr::phylum_cast(primary_alternative())
				 ->int_const_expr(ctx, value);
    }
    public virtual method bool has_addr(TypecheckContext *ctx) {
	return expr::phylum_cast(primary_alternative())->has_addr(ctx);
    }
}

phylum arg_expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return ExprType(ERROR_CLASS, 0);
    }
}

operator ExpressionArgument extends arg_expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return get_expr()->determine_type(ctx);
    }
}

operator arg_expr_sym extends arg_expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return arg_expr::phylum_cast(primary_alternative())
	    ->determine_type(ctx);
    }
}

phylum designation {
}

// designation -> SeqR41E0star:desigs ASSIGN 
operator Designation extends designation {
}

operator no_designation extends designation {
}

operator designation_sym extends designation {
}

phylum inner_init {
}

// inner_init -> OptR42E0:desig init_:init 
operator InnerInitializer extends inner_init {
}

operator no_inner_init extends inner_init {
}

operator inner_init_sym extends inner_init {
}

phylum assign_expr depends on expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return ExprType(ERROR_CLASS, 0);
    }

    public virtual method bool int_const_expr(TypecheckContext *ctx,
					      bignum_t *value) {
	return false;
    }
    public virtual method bool has_addr(TypecheckContext *ctx) {
	return false;
    }
}

// assign_expr -> expr:left ASSIGN assign_expr:right 
operator NormalAssign extends assign_expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType left, right, tmp;
	if (!(left = get_left()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if (!(right = get_right()->determine_type(ctx)))
	    return ExprType(ERROR_CLASS, 0);
	if ((tmp = left.to_mod_lvalue())) {
	    left = tmp;
	} else {
	    set_semant_error(this, "Can't assign to %s", left.name().c_str());
	}
	if ((tmp = right.to_rvalue())) {
	    right = tmp;
	} else {
	    set_semant_error(this, "Can't use %s as right arg to `='",
			     right.name().c_str());
	}
	CType *left_uq = left.ty->unqualified();
	if (ExprType::assign_compat(left_uq, right, "assigment", this)) {
	    ExprType result;
	    result.ty = left_uq;
	    result.cl = RVALUE;
	    string msg = "Assignment has type " + result.ty->name();
	    set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	    compute_synth_attrs(true);
	    return result;
	} else {
	    return ExprType(ERROR_CLASS, 0);
	}
    }
}

// assign_expr -> expr:left MULASSIGN assign_expr:right 
operator MultiplyAssign extends assign_expr {
}

// assign_expr -> expr:left DIVASSIGN assign_expr:right 
operator DivideAssign extends assign_expr {
}

// assign_expr -> expr:left MODASSIGN assign_expr:right 
operator ModAssign extends assign_expr {
}

// assign_expr -> expr:left ADDASSIGN assign_expr:right 
operator AddAssign extends assign_expr {
}

// assign_expr -> expr:left SUBASSIGN assign_expr:right 
operator SubtractAssign extends assign_expr {
}

// assign_expr -> expr:left SHLASSIGN assign_expr:right 
operator ShiftLeftAssign extends assign_expr {
}

// assign_expr -> expr:left SHRASSIGN assign_expr:right 
operator ShiftRightAssign extends assign_expr {
}

// assign_expr -> expr:left ANDASSIGN assign_expr:right 
operator AndAssign extends assign_expr {
}

// assign_expr -> expr:left XORASSIGN assign_expr:right 
operator XorAssign extends assign_expr {
}

// assign_expr -> expr:left ORASSIGN assign_expr:right 
operator OrAssign extends assign_expr {
}

// assign_expr -> expr:expr 
operator SimpleExpression extends assign_expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	ExprType type = get_expr()->determine_type(ctx);
// 	string msg = "Simple expression has type " + type->name();
// 	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
//	compute_synth_attrs(true);
	return type;
    }

    public virtual method bool int_const_expr(TypecheckContext *ctx,
					      bignum_t *value) {
	return get_expr()->int_const_expr(ctx, value);
    }
    public virtual method bool has_addr(TypecheckContext *ctx) {
	return get_expr()->has_addr(ctx);
    }
}

operator no_assign_expr extends assign_expr {
}

operator assign_expr_sym extends assign_expr {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return assign_expr::phylum_cast(primary_alternative())
	    ->determine_type(ctx);
    }
    public virtual method bool int_const_expr(TypecheckContext *ctx,
					      bignum_t *value) {
	return assign_expr::phylum_cast(primary_alternative())
	    ->int_const_expr(ctx, value);
    }
    public virtual method bool has_addr(TypecheckContext *ctx) {
	return assign_expr::phylum_cast(primary_alternative())->has_addr(ctx);
    }
}

phylum pointer {
    public virtual method CType *point_to(TypecheckContext *ctx,
					  CType *base_type) {
	return base_type;
    }
}

// pointer -> TIMES SeqR51E2star:quals OptR46E0:ptr 
operator Pointer extends pointer depends on decl {
    public virtual method CType *point_to(TypecheckContext *ctx,
					  CType *base_type) {
	decl::TypeInfo quals_info;
	CType *type = base_type->pointer_to();
	for (quals_iterator i = get_quals(); i; ++i)
	    i.get_qual()->specify(&quals_info);
	type = type->qualified_by(quals_info.quals);
	if (has_ptr())
	    return get_ptr_ptr()->point_to(ctx, type);
	else
	    return type;
    }
}

operator no_pointer extends pointer {
}

operator pointer_sym extends pointer {
    public virtual method CType *point_to(TypecheckContext *ctx,
					  CType *base_type) {
	return pointer::phylum_cast(primary_alternative())
	    ->point_to(ctx, base_type);
    }
}

phylum direct_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	return base_type;
    }
}

// direct_declr -> IDSYM:name 
operator DeclarNameDirDeclr extends direct_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *type) {
	string name = get_name()->pooled_string().chars();
// 	if (!type->is_complete())
// 	    set_semant_error(this, "Can't declare variable `%s' with "
// 			     "incomplete type %s", name.c_str(),
// 			     type->name().c_str());
	string msg = name + " has type " + type->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
	return type;
    }
}

// direct_declr -> LPAREN OptR46E0:ptr direct_declr:direct RPAREN 
operator ParenNameDirDeclr extends direct_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	CType *type = base_type;
	if (has_ptr())
	    type = get_ptr_ptr()->point_to(ctx, type);
	type = get_direct()->build_type(ctx, type);
	return type;
    }
}

// direct_declr -> direct_declr:declr LBRACK SeqR51E2star:quals OptR51E3:expr RBRACK 
operator BasicArrayDirDeclr extends direct_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	if (base_type->is_function())
	    set_semant_error(this,
			     "Array elements may not have function type");
	if (!base_type->is_complete())
	    set_semant_error(this,
			     "Array elements may not have incomplete type");
	decl::TypeInfo decl_info;
	for (quals_iterator i = get_quals(); i; ++i)
	    i.get_qual()->specify(&decl_info);
	unsigned quals = decl_info.quals;
	CType *type;
	if (has_expr()) {
	    ExprType size_type = get_expr_expr()->determine_type(ctx);
	    bignum_t size;
	    if (size_type.is_int_const()) {
		get_expr_expr()->int_const_expr(ctx, &size);
	    } else if (size_type && size_type.ty->is_integer()) {
		quals |= VARIABLE_LEN_QUAL;
	    } else {
		set_semant_error(this, "Array size expression must have "
				 "integer type");
		size = 1;
	    }
	    type = base_type->array_of(size, quals);
	} else {
	    type = base_type->array_of(0, quals|INCOMPLETE_QUAL);
	}
	return get_declr()->build_type(ctx, type);
    }
}

// direct_declr -> direct_declr:declr LBRACK SeqR51E2star:quals1 STATIC SeqR51E2star:quals2 assign_expr:expr RBRACK 
operator StaticArrayDirDeclr extends direct_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	if (base_type->is_function())
	    set_semant_error(this,
			     "Array elements may not have function type");
	if (!base_type->is_complete())
	    set_semant_error(this,
			     "Array elements may not have incomplete type");
	decl::TypeInfo decl_info;
	for (quals1_iterator i = get_quals1(); i; ++i)
	    i.get_qual()->specify(&decl_info);
	for (quals2_iterator i = get_quals2(); i; ++i)
	    i.get_qual()->specify(&decl_info);
	unsigned quals = decl_info.quals;
	ExprType size_type = get_expr()->determine_type(ctx);
	bignum_t size;
	if (size_type.is_int_const()) {
	    get_expr()->int_const_expr(ctx, &size);
	} else if (size_type && size_type.ty->is_integer()) {
	    quals |= VARIABLE_LEN_QUAL;
	} else {
	    set_semant_error(this, "Array size expression must have "
			     "integer type");
	    size = 1;
	}
	CType *type = base_type->array_of(size, quals|STATIC_QUAL);
	return get_declr()->build_type(ctx, type);
    }
}

// direct_declr -> direct_declr:declr LBRACK SeqR51E2star:quals TIMES RBRACK 
operator VariableArrayDirDeclr extends direct_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	if (base_type->is_function())
	    set_semant_error(this,
			     "Array elements may not have function type");
	if (!base_type->is_complete())
	    set_semant_error(this,
			     "Array elements may not have incomplete type");
	decl::TypeInfo decl_info;
	for (quals_iterator i = get_quals(); i; ++i)
	    i.get_qual()->specify(&decl_info);
	unsigned quals = decl_info.quals;
	CType *type = base_type->array_of(0, quals|VARIABLE_LEN_QUAL);
	return get_declr()->build_type(ctx, type);
    }
}

// direct_declr -> direct_declr:declr LPAREN param_type_list:params RPAREN 
operator TypeFuncListDirDeclr extends direct_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	if (base_type->is_function())
	    set_semant_error(this, "Function may not return function type");
	if (base_type->is_array())
	    set_semant_error(this, "Function may not return array type");
	CType *type = get_params()->build_type(ctx, base_type);
	return get_declr()->build_type(ctx, type);
    }
}

// direct_declr -> direct_declr:declr LPAREN SeqR55E2star:idents RPAREN 
operator IDFuncListDirDeclr extends direct_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	if (base_type->is_function())
	    set_semant_error(this, "Function may not return function type");
	if (base_type->is_array())
	    set_semant_error(this, "Function may not return array type");
	type_vector no_params;
	CType *type = base_type->function_taking(UNKNOWN_ARGS, no_params);
	return get_declr()->build_type(ctx, type);
    }
}

operator no_direct_declr extends direct_declr {
}

operator direct_declr_sym extends direct_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	return direct_declr::phylum_cast(primary_alternative())
	    ->build_type(ctx, base_type);
    }
}

phylum direct_abst_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	return base_type;
    }
}

// direct_abst_declr -> LPAREN abst_declr:declr RPAREN 
operator ParenAbstDecl extends direct_abst_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	return get_declr()->build_type(ctx, base_type);
    }
}

// direct_abst_declr -> OptR57E0:declr LBRACK OptR51E3:expr RBRACK 
operator ArrayAbstDecl extends direct_abst_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	if (base_type->is_function())
	    set_semant_error(this,
			     "Array elements may not have function type");
	if (!base_type->is_complete())
	    set_semant_error(this,
			     "Array elements may not have incomplete type");
	unsigned quals = 0;
	CType *type;
	if (has_expr()) {
	    ExprType size_type = get_expr_expr()->determine_type(ctx);
	    bignum_t size;
	    if (size_type.is_int_const()) {
		get_expr_expr()->int_const_expr(ctx, &size);
	    } else if (size_type && size_type.ty->is_integer()) {
		quals |= VARIABLE_LEN_QUAL;
	    } else {
		set_semant_error(this, "Array size expression must have "
				 "integer type");
		size = 1;
	    }
	    type = base_type->array_of(size, quals);
	} else {
	    type = base_type->array_of(0, quals|INCOMPLETE_QUAL);
	}
	if (has_declr())
	    return get_declr_declr()->build_type(ctx, type);
	else
	    return type;
    }
}

// direct_abst_declr -> OptR57E0:declr LBRACK TIMES RBRACK 
operator VariableAbstDecl extends direct_abst_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	if (base_type->is_function())
	    set_semant_error(this,
			     "Array elements may not have function type");
	if (!base_type->is_complete())
	    set_semant_error(this,
			     "Array elements may not have incomplete type");
	CType *type = base_type->array_of(0, VARIABLE_LEN_QUAL);
	if (has_declr())
	    return get_declr_declr()->build_type(ctx, type);
	else
	    return type;
    }
}

// direct_abst_declr -> direct_abst_declr:declr LPAREN OptR59E2:params RPAREN 
operator NestedFuncAbstDecl extends direct_abst_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	if (base_type->is_function())
	    set_semant_error(this, "Function may not return function type");
	if (base_type->is_array())
	    set_semant_error(this, "Function may not return array type");
	CType *type;
	if (has_params()) {
	    type = get_params_params()->build_type(ctx, base_type);
	} else {
	    type_vector no_params;
	    type = base_type->function_taking(UNKNOWN_ARGS, no_params);
	}
	return get_declr()->build_type(ctx, type);
    }
}

// direct_abst_declr -> LPAREN OptR59E2:params RPAREN 
operator FuncAbstDecl extends direct_abst_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	if (base_type->is_function())
	    set_semant_error(this, "Function may not return function type");
	if (base_type->is_array())
	    set_semant_error(this, "Function may not return array type");
	CType *type;
	if (has_params()) {
	    type = get_params_params()->build_type(ctx, base_type);
	} else {
	    type_vector no_params;
	    type = base_type->function_taking(UNKNOWN_ARGS, no_params);
	}
	return type;
    }
}

operator no_direct_abst_declr extends direct_abst_declr {
}

operator direct_abst_declr_sym extends direct_abst_declr {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	return direct_abst_declr::phylum_cast(primary_alternative())
	    ->build_type(ctx, base_type);
    }
}

phylum param_type_list {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	return base_type;
    }
}

// param_type_list -> SeqR67E0star:decls OptR67E1:dotdotdot 
operator ParameterTypeList extends param_type_list {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	unsigned flags = 0;
	type_vector params;
	if (has_dotdotdot())
	    flags |= ELLIPTICAL;
	for (decls_iterator i = get_decls(); i; ++i)
	    params.push_back(i.get_decl()->get_type(ctx));
	if (params.size() == 1 && params[0]->is_void()) {
	    params = type_vector();
	} else {
	    for (type_vector::iterator i = params.begin();
		 i != params.end(); i++) {
		if (!(*i)->is_complete()) {
		    set_semant_error(this, "Function declared with incomplete "
				     "argument type %s", (*i)->name().c_str());
		}
	    }
	}
	return base_type->function_taking(flags, params);
    }
}

operator no_param_type_list extends param_type_list {
}

operator param_type_list_sym extends param_type_list {
    public virtual method CType *build_type(TypecheckContext *ctx,
					    CType *base_type) {
	return param_type_list::phylum_cast(primary_alternative())
	    ->build_type(ctx, base_type);
    }
}

phylum struct_declr {
    public virtual method void check_member(TypecheckContext *ctx,
					    CompleteStructUnionType *members,
					    CType *base_type) {	
    }
}

// struct_declr -> declr:declr 
operator StructDeclr extends struct_declr {
    public virtual method void check_member(TypecheckContext *ctx,
					    CompleteStructUnionType *members,
					    CType *base_type) {
	CType *type = get_declr()->build_type(ctx, base_type);
	IDSYM *name_node = get_declr()->find_name();
	PooledString ident = name_node->pooled_string();
	members->table.add(ident, type);
    }
}

// struct_declr -> OptR63E0:declr COLON expr:expr 
operator BitfieldDeclr extends struct_declr {
    public virtual method void check_member(TypecheckContext *ctx,
					    CompleteStructUnionType *members,
					    CType *base_type) {
	if (!base_type->unqualified()->is_integer()) {
	    set_semant_error(this, "Bitfield must have integer type, not %s",
			     base_type->name().c_str());
	    return;
	}
	unsigned quals = base_type->qualifiers();
	IntegerType *int_type = (IntegerType *)base_type->unqualified();
	if (!int_type->bitfield_okay()) {
	    set_semant_error(this, "Bitfield must have _Bool, int, or "
			     "unsigned int type, not %s",
			     int_type->name().c_str());
	    return;
	}
	ExprType width_type = get_expr()->determine_type(ctx);
	bignum_t width;
	if (width_type.is_int_const()) {
	    get_expr()->int_const_expr(ctx, &width);
	} else {
	    set_semant_error(this, "Bitfield width expression must be "
			     "an integer constant");
	    width = 1;
	}
	if (width > int_type->bitfield_width()) {
	    set_semant_error(this, "Bitfield width cannot be larger than "
			     "width (%d bit%s) of %s",
			     int_type->bitfield_width(),
			     (int_type->bitfield_width() > 1 ? "s" : ""),
			     int_type->name().c_str());
	    width = 1;
	}
	base_type = int_type->bitfield_of(width)->qualified_by(quals);
	if (has_declr()) {
	    CType *type = get_declr_declr()->build_type(ctx, base_type);
	    IDSYM *name_node = get_declr_declr()->find_name();
	    PooledString ident = name_node->pooled_string();
	    members->table.add(ident, type);
	}
    }
}

operator no_struct_declr extends struct_declr {
}

operator struct_declr_sym extends struct_declr {
    public virtual method void check_member(TypecheckContext *ctx,
					    CompleteStructUnionType *members,
					    CType *base_type) {	
	struct_declr::phylum_cast(primary_alternative())
	    ->check_member(ctx, members, base_type);
    }
}

phylum param_decl {
    public virtual method CType *get_type(TypecheckContext *ctx) {
	return &ctx->the_UnimplementedType;
    }
}

// param_decl -> SeqR1E0star:specs declr:declr 
operator NamedParamDecl extends param_decl {
    public virtual method CType *get_type(TypecheckContext *ctx) {
	decl::TypeInfo base_info;
	for (specs_iterator i = get_specs(); i; ++i)
	    i.get_spec()->specify(ctx, &base_info);
	CType *base_type = decl::assemble_base_type(ctx, this, &base_info);
	return get_declr()->build_type(ctx, base_type);
    }
}

// param_decl -> SeqR1E0star:specs OptR13E1:declr 
operator UnnamedParamDecl extends param_decl {
    public virtual method CType *get_type(TypecheckContext *ctx) {
	decl::TypeInfo base_info;
	for (specs_iterator i = get_specs(); i; ++i)
	    i.get_spec()->specify(ctx, &base_info);
	CType *base_type = decl::assemble_base_type(ctx, this, &base_info);
	if (has_declr())
	    return get_declr_declr()->build_type(ctx, base_type);
	else
	    return base_type;
    }
// 	    if (ctx->decl_cx == DEF_PARAM &&
// 		base_type.spec_bits != decl::VOID)
// 		set_semant_error(this, "Unnamed params not allowed in "
// 			     "function definition");
}

operator no_param_decl extends param_decl {
}

operator param_decl_sym extends param_decl {
    public virtual method CType *get_type(TypecheckContext *ctx) {
	return param_decl::phylum_cast(primary_alternative())->get_type(ctx);
    }
}

phylum for_init {
    public virtual method void check_types(TypecheckContext *ctx) {
    }
}

// for_init -> OptR68E0:expr SEMI 
operator ExpressionInit extends for_init {
    public virtual method void check_types(TypecheckContext *ctx) {
	if (has_expr()) {
	    ExprType type = get_expr_expr()->determine_type(ctx);
	}
    }
}

// for_init -> decl:decl 
operator DeclarationInit extends for_init {
    public virtual method void check_types(TypecheckContext *ctx) {
	get_decl()->check_types(ctx);
    }
}

operator no_for_init extends for_init {
}

operator for_init_sym extends for_init {
    public virtual method void check_types(TypecheckContext *ctx) {
	for_init::phylum_cast(primary_alternative())->check_types(ctx);
    }
}

phylum exprs {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return ExprType(ERROR_CLASS, 0);
    }
    public virtual method bool has_addr(TypecheckContext *ctx) {
	return false;
    }
}

// exprs -> exprs:left COMMA assign_expr:right 
operator CommaList extends exprs {
}

// exprs -> assign_expr:expr 
operator SingleExpr extends exprs {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return get_expr()->determine_type(ctx);
    }
    public virtual method bool has_addr(TypecheckContext *ctx) {
	return get_expr()->has_addr(ctx);
    }
}

operator no_exprs extends exprs {
}

operator exprs_sym extends exprs {
    public virtual method ExprType determine_type(TypecheckContext *ctx) {
	return exprs::phylum_cast(primary_alternative())->determine_type(ctx);
    }
    public virtual method bool has_addr(TypecheckContext *ctx) {
	return exprs::phylum_cast(primary_alternative())->has_addr(ctx);
    }
}

phylum stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
    }
}

// stmt -> IDSYM:label COLON stmt:stmt 
operator LabeledStatement extends stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	get_stmt()->check_types(ctx);
    }
}

// stmt -> CASE expr:expr COLON stmt:stmt 
operator CaseLabelStatement extends stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	ExprType type = get_expr()->determine_type(ctx);
	if (type && !type.is_int_const())
	    set_semant_error(this, "Case label expression must be integer "
			     "constant expression");
	get_stmt()->check_types(ctx);
    }
}

// stmt -> DEFAULT COLON stmt:stmt 
operator DefaultLabelStatement extends stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	get_stmt()->check_types(ctx);
    }
}

// stmt -> OptR68E0:expr SEMI 
operator ExprStatement extends stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	if (has_expr()) {
	    ExprType type = get_expr_expr()->determine_type(ctx);
	}
    }
}

// stmt -> comp_stmt:stmt 
operator CompoundStatement extends stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	get_stmt()->check_types(ctx);
    }
}

// stmt -> IF LPAREN exprs:pred RPAREN stmt:t_stmt 
operator IfNoElseStatement extends stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	ExprType type = get_pred()->determine_type(ctx);
	if (type && !type.ty->is_scalar())
	    set_semant_error(this, "Predicate in `if' statement must have "
			     "scalar type, not %s", type.ty->name().c_str());
	get_t_stmt()->check_types(ctx);
    }
}

// stmt -> IF LPAREN exprs:pred RPAREN stmt:t_stmt ELSE stmt:f_stmt 
operator IfElseStatement extends stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	ExprType type = get_pred()->determine_type(ctx);
	if (type && !type.ty->is_scalar())
	    set_semant_error(this, "Predicate in `if' statement must have "
			     "scalar type, not %s", type.ty->name().c_str());
	get_t_stmt()->check_types(ctx);
	get_f_stmt()->check_types(ctx);
    }
}

// stmt -> SWITCH LPAREN exprs:val RPAREN stmt:stmt 
operator SwitchStatement extends stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	ExprType type = get_val()->determine_type(ctx);
	if (type && !type.ty->is_integer())
	    set_semant_error(this, "Expression in `switch' statement must have"
			     " integer  type, not %s",
			     type.ty->name().c_str());
	get_stmt()->check_types(ctx);
    }
}

// stmt -> WHILE LPAREN exprs:val RPAREN stmt:stmt 
operator WhileStatement extends stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	ExprType type = get_val()->determine_type(ctx);
	if (type && !type.ty->is_scalar())
	    set_semant_error(this, "Predicate in `while' statement must have "
			     "scalar type, not %s", type.ty->name().c_str());
	get_stmt()->check_types(ctx);
    }
}

// stmt -> DO stmt:stmt WHILE LPAREN exprs:val RPAREN SEMI 
operator DoWhileStatement extends stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	ExprType type = get_val()->determine_type(ctx);
	if (type && !type.ty->is_scalar())
	    set_semant_error(this, "Predicate in do-while statement must have "
			     "scalar type, not %s", type.ty->name().c_str());
	get_stmt()->check_types(ctx);
    }
}

// stmt -> FOR LPAREN for_init:init OptR80E3:pred SEMI OptR80E5:inc RPAREN stmt:stmt 
operator ForStatement extends stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	get_init()->check_types(ctx);
	if (has_pred()) {
	    ExprType type = get_pred_expr()->determine_type(ctx);
	    if (type && !type.ty->is_scalar())
		set_semant_error(this, "Predicate in `for' statement must"
				 " have scalar type, not %s",
				 type.ty->name().c_str());
	}
	if (has_inc()) {
	    ExprType type = get_inc_expr()->determine_type(ctx);
	}
	get_stmt()->check_types(ctx);
    }
}

// stmt -> GOTO IDSYM:label SEMI 
operator GotoStatement extends stmt {
}

// stmt -> CONTINUE SEMI 
operator ContinueStatement extends stmt {
}

// stmt -> BREAK SEMI 
operator BreakStatement extends stmt {
}

// stmt -> RETURN OptR84E1:ret SEMI 
operator ReturnStatement extends stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	if (has_ret()) {
	    ExprType tmp, type = get_ret_expr()->determine_type(ctx);
	    if (!type)
		return;
	    if ((tmp = type.to_rvalue())) {
		type = tmp;
	    } else {
		set_semant_error(this, "Can't use %s as arg to return",
				 type.name().c_str());
	    }
	    if (ctx->return_type->is_void()) {
		set_semant_error(this, "Attempt to return a value of type %s "
				 "from a function declared to return void",
				 type.ty->name().c_str());
	    } else {
		// This sets its own error messages
		ExprType::assign_compat(ctx->return_type->unqualified(), type,
					"return", this);
	    }
	} else {
	    if (!ctx->return_type->is_void()) {
		set_semant_error(this, "Return of no value from a function "
				 "declared to return %s",
				 ctx->return_type->name().c_str());
		return;
	    }
	}
	string msg = "Return type declared to be " + ctx->return_type->name();
	set_string_prop(MOUSE_OVER_MSG, msg.c_str());
	compute_synth_attrs(true);
    }
}

operator no_stmt extends stmt {
}

operator stmt_sym extends stmt {
    public virtual method void check_types(TypecheckContext *ctx) {
	stmt::phylum_cast(primary_alternative())->check_types(ctx);
    }
}

phylum block_item {
    public virtual method void check_types(TypecheckContext *ctx) {
    }
}

// block_item -> decl:decl 
operator DeclarationItem extends block_item {
    public virtual method void check_types(TypecheckContext *ctx) {
	get_decl()->check_types(ctx);
    }
}

// block_item -> stmt:stmt 
operator StatementItem extends block_item {
    public virtual method void check_types(TypecheckContext *ctx) {
	get_stmt()->check_types(ctx);
    }
}

operator no_block_item extends block_item {
}

operator block_item_sym extends block_item {
    public virtual method void check_types(TypecheckContext *ctx) {
	block_item::phylum_cast(primary_alternative())->check_types(ctx);
    }
}

phylum int_const {
    public virtual method bignum_t get_value(TypecheckContext *ctx,
					 bool *decimal) {
	return 0;
    }
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	return &ctx->the_UnimplementedType;
    }
    public static method bignum_t parse_int(const char *s, bool *decimal) {
	bignum_t value;
	if (s[0] == '0') {
	    if (!s[1] || s[1] == 'u' || s[1] == 'U' ||
		         s[1] == 'l' || s[1] == 'L') {
		*decimal = true;
		return 0;
	    } else if (s[1] == 'x' || s[1] == 'X') {
		*decimal = false;
		sscanf(&s[2], "%llx", &value);
		return value;
	    } else if (s[1] >= '1' && s[1] <= '9') {
		*decimal = false;
		sscanf(&s[1], "%llo", &value);
		return value;
	    } else {
		// error("Unexpected character after '0' in integer\n");
		return 0;
	    }
	} else {
	    // assert(s[0] >= '1' && s[0] <= '9');
	    *decimal = true;
	    sscanf(s, "%lld", &value);
	    return value;
	}
    }
}

// int_const -> INT_CONST:it 
operator PlainIntegerConstant extends int_const {
    public virtual method bignum_t get_value(TypecheckContext *ctx,
					     bool *decimal) {
	const char *s = get_it()->pooled_string().chars();
	return int_const::parse_int(s, decimal);
    }

    public virtual method CType *determine_type(TypecheckContext *ctx) {
	bool decimal;
	bignum_t value = get_value(ctx, &decimal);
	if (decimal) {
	    if (value <= ctx->the_SignedIntType.max_val())
		return &ctx->the_SignedIntType;
	    else if (value <= ctx->the_LongIntType.max_val())
		return &ctx->the_LongIntType;
	    else if (value <= ctx->the_LongLongIntType.max_val())
		return &ctx->the_LongLongIntType;
	    else {
		set_semant_error(this, "Integer constant too big");
		return &ctx->the_LongLongIntType;
	    }
	} else {
	    if (value <= ctx->the_SignedIntType.max_val())
		return &ctx->the_SignedIntType;
	    else if (value <= ctx->the_UnsignedIntType.max_val())
		return &ctx->the_UnsignedIntType;
	    else if (value <= ctx->the_LongIntType.max_val())
		return &ctx->the_LongIntType;
	    else if (value <= ctx->the_UnsignedLongType.max_val())
		return &ctx->the_UnsignedLongType;
	    else if (value <= ctx->the_LongLongIntType.max_val())
		return &ctx->the_LongLongIntType;
	    else if (value <= ctx->the_UnsignedLongLongType.max_val())
		return &ctx->the_UnsignedLongLongType;
	    else {
		set_semant_error(this, "Integer constant too big");
		return &ctx->the_UnsignedLongLongType;
	    }
	}
    }
}

// int_const -> INT_CONST_U:it 
operator UnsignedIntegerConstant extends int_const {
    public virtual method bignum_t get_value(TypecheckContext *ctx,
					     bool *decimal) {
	const char *s = get_it()->pooled_string().chars();
	return int_const::parse_int(s, decimal);
    }

    public virtual method CType *determine_type(TypecheckContext *ctx) {
	bool decimal;
	bignum_t value = get_value(ctx, &decimal);
	if (value <= ctx->the_UnsignedIntType.max_val())
	    return &ctx->the_UnsignedIntType;
	else if (value <= ctx->the_UnsignedLongType.max_val())
	    return &ctx->the_UnsignedLongType;
	else if (value <= ctx->the_UnsignedLongLongType.max_val())
	    return &ctx->the_UnsignedLongLongType;
	else {
	    set_semant_error(this, "Integer constant too big");
	    return &ctx->the_UnsignedLongLongType;
	}
    }
}

// int_const -> INT_CONST_L:it 
operator LongIntegerConstant extends int_const {
    public virtual method bignum_t get_value(TypecheckContext *ctx,
					     bool *decimal) {
	const char *s = get_it()->pooled_string().chars();
	return int_const::parse_int(s, decimal);
    }

    public virtual method CType *determine_type(TypecheckContext *ctx) {
	bool decimal;
	bignum_t value = get_value(ctx, &decimal);
	if (decimal) {
	    if (value <= ctx->the_LongIntType.max_val())
		return &ctx->the_LongIntType;
	    else if (value <= ctx->the_LongLongIntType.max_val())
		return &ctx->the_LongLongIntType;
	    else {
		set_semant_error(this, "Integer constant too big");
		return &ctx->the_LongLongIntType;
	    }
	} else {
	    if (value <= ctx->the_LongIntType.max_val())
		return &ctx->the_LongIntType;
	    else if (value <= ctx->the_UnsignedLongType.max_val())
		return &ctx->the_UnsignedLongType;
	    else if (value <= ctx->the_LongLongIntType.max_val())
		return &ctx->the_LongLongIntType;
	    else if (value <= ctx->the_UnsignedLongLongType.max_val())
		return &ctx->the_UnsignedLongLongType;
	    else {
		set_semant_error(this, "Integer constant too big");
		return &ctx->the_UnsignedLongLongType;
	    }
	}
    }
}

// int_const -> INT_CONST_UL:it 
operator UnsignedLongIntegerConstant extends int_const {
    public virtual method bignum_t get_value(TypecheckContext *ctx,
					     bool *decimal) {
	const char *s = get_it()->pooled_string().chars();
	return int_const::parse_int(s, decimal);
    }

    public virtual method CType *determine_type(TypecheckContext *ctx) {
	bool decimal;
	bignum_t value = get_value(ctx, &decimal);
	if (value <= ctx->the_UnsignedLongType.max_val())
	    return &ctx->the_UnsignedLongType;
	else if (value <= ctx->the_UnsignedLongLongType.max_val())
	    return &ctx->the_UnsignedLongLongType;
	else {
	    set_semant_error(this, "Integer constant too big");
	    return &ctx->the_UnsignedLongLongType;
	}
    }
}

// int_const -> INT_CONST_LL:it 
operator LongLongIntegerConstant extends int_const {
    public virtual method bignum_t get_value(TypecheckContext *ctx,
					     bool *decimal) {
	const char *s = get_it()->pooled_string().chars();
	return int_const::parse_int(s, decimal);
    }

    public virtual method CType *determine_type(TypecheckContext *ctx) {
	bool decimal;
	bignum_t value = get_value(ctx, &decimal);
	if (decimal) {
	    if (value <= ctx->the_LongLongIntType.max_val())
		return &ctx->the_LongLongIntType;
	    else {
		set_semant_error(this, "Integer constant too big");
		return &ctx->the_LongLongIntType;
	    }
	} else {
	    if (value <= ctx->the_LongLongIntType.max_val())
		return &ctx->the_LongLongIntType;
	    else if (value <= ctx->the_UnsignedLongLongType.max_val())
		return &ctx->the_UnsignedLongLongType;
	    else {
		set_semant_error(this, "Integer constant too big");
		return &ctx->the_UnsignedLongLongType;
	    }
	}
    }
}

// int_const -> INT_CONST_ULL:it 
operator UnsignedLongLongIntegerConstant extends int_const {
    public virtual method bignum_t get_value(TypecheckContext *ctx,
					     bool *decimal) {
	const char *s = get_it()->pooled_string().chars();
	return int_const::parse_int(s, decimal);
    }

    public virtual method CType *determine_type(TypecheckContext *ctx) {
	bool decimal;
	bignum_t value = get_value(ctx, &decimal);
	if (value <= ctx->the_UnsignedLongLongType.max_val())
	    return &ctx->the_UnsignedLongLongType;
	else {
	    set_semant_error(this, "Integer constant too big");
	    return &ctx->the_UnsignedLongLongType;
	}
    }
}

operator no_int_const extends int_const {
}

operator int_const_sym extends int_const {
    public virtual method bignum_t get_value(TypecheckContext *ctx,
					     bool *decimal) {
	return int_const::phylum_cast(primary_alternative())
	    ->get_value(ctx, decimal);
    }

    public virtual method CType *determine_type(TypecheckContext *ctx) {
	return int_const::phylum_cast(primary_alternative())
	    ->determine_type(ctx);
    }
}

phylum char_const {
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	return &ctx->the_UnimplementedType;
    }
}

// char_const -> CHAR_CONST:it 
operator PlainCharacterConstant extends char_const {
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	return &ctx->the_SignedIntType;
    }
}

// char_const -> WIDE_CHAR_CONST:it 
operator WideCharacterConstant extends char_const {
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	return ctx->the_WideCharType;
    }
}

operator no_char_const extends char_const {
}

operator char_const_sym extends char_const {
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	return char_const::phylum_cast(primary_alternative())
	    ->determine_type(ctx);
    }
}

phylum float_const {
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	return &ctx->the_UnimplementedType;
    }
}

// float_const -> FLOAT_CONST:it 
operator FloatConstant extends float_const {
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	return &ctx->the_FloatType;
    }
}

// float_const -> DOUBLE_CONST:it 
operator DoubleConstant extends float_const {
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	return &ctx->the_DoubleType;
    }
}

// float_const -> LONGDOUBLE_CONST:it 
operator LongDoubleConstant extends float_const {
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	return &ctx->the_LongDoubleType;
    }
}

operator no_float_const extends float_const {
}

operator float_const_sym extends float_const {
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	return float_const::phylum_cast(primary_alternative())
	    ->determine_type(ctx);
    }
}

phylum str_const {
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	return &ctx->the_UnimplementedType;
    }

    public static method string unquote(PooledString ps) {
	string s = ps.chars();
	s = s.substr(1, s.length() - 2);
	return s;
    }
    public static method int real_length(const char *s) {
	const char *p = s;
	int len;
	for (len = 0; *p; len++) {
	    if (*p == '\\') {
		p++;
		switch (*p) {
		case 'u': p += 4+1; break;
		case 'U': p += 8+1; break;
		case 'x':
		    p++;
		    while (isxdigit(*p))
			p++;
		    break;
		case '0': case '1': case '2': case '3':
		case '4': case '5': case '6': case '7':
		    p++;
		    if (*p >= '0' && *p < '8')
			p++;
		    if (*p >= '0' && *p < '8')
			p++;
		    break;
		default:
		    p++;
		}
	    } else {
		p++;
	    }
	}
	return len;
    }
}

// str_const -> SeqR151E0star:strs 
operator PlainStringConstant extends str_const {
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	int len = 0;
	for (strs_iterator i = get_strs(); i; ++i)
	    len += real_length(unquote(i.get_str()->pooled_string()).c_str());
	return ctx->the_PlainCharType.array_of(len + 1);
    }
}

// str_const -> SeqR152E0star:strs 
operator WideStringConstant extends str_const {
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	int len = 0;
	for (strs_iterator i = get_strs(); i; ++i)
	    len += real_length(unquote(i.get_str()->pooled_string()).c_str());
	return ctx->the_WideCharType->array_of(len + 1);
    }
}

operator no_str_const extends str_const {
}

operator str_const_sym extends str_const {
    public virtual method CType *determine_type(TypecheckContext *ctx) {
	return str_const::phylum_cast(primary_alternative())
	    ->determine_type(ctx);
    }
}