+ + +

The mechanism by which Inform records the characteristics of different kinds.

+ +
+ +

§1. Constructors are divided into four: +

+ +
define KIND_VARIABLE_GRP 1  just CON_KIND_VARIABLE on its own
+define KIND_OF_KIND_GRP 2  an indefinite base constructor such as "arithmetic value"
+define BASE_CONSTRUCTOR_GRP 3  a definite one such as "number"
+define PROPER_CONSTRUCTOR_GRP 4  with positive arity, such as "list of K"
+
+

§2. Inform provides much more extensive facilities for kinds than most +programming languages do for their types. As far as possible, we want to +write this code in a generalised way, rather than writing hacky routines +which each apply to a fixed, named kind. This won't always be possible, it's +true, but we can try. The only way to achieve this is to store an enormous +rag-bag of properties for every kind constructor, showing exactly how it +behaves. +

+ +

All of which is by way of apology for the enormous size of the kind_constructor +structure. It looks impossibly large to fill out, and this is why we have the +kind interpreter (see next section) to give the I6 template the ability to +forge new kind constructors. +

+ +
define LOWEST_INDEX_PRIORITY 100
+define MAX_KIND_CONSTRUCTION_ARITY 2
+
+
+typedef struct kind_constructor {
+    struct noun *dt_tag;  text of name
+    int group;  one of the four values above
+
+     A: how this came into being
+    int defined_in_source_text;  rather than by I6 template files, i.e., by being built-in
+    int is_incompletely_defined;  newly defined and ambiguous as yet
+    struct parse_node *where_defined_in_source_text;  if so
+    struct kind *stored_as;  currently unused: if this is a typedef for some construction
+
+     B: constructing kinds
+    int constructor_arity;  0 for base, 1 for unary, 2 for binary
+    int variance[MAX_KIND_CONSTRUCTION_ARITY];  must be COVARIANT or CONTRAVARIANT
+    int tupling[MAX_KIND_CONSTRUCTION_ARITY];  extent to which tupling is permitted
+    struct kind *cached_kind;  cached result of Kinds::base_construction
+
+     C: compatibility with other kinds
+    struct parse_node *superkind_set_at;  where it says, e.g., "A rabbit is a kind of animal"
+    struct kind_constructor_casting_rule *first_casting_rule;  list of these
+    struct kind_constructor_instance *first_instance_rule;  list of these
+
+     D: how constant values of this kind are expressed
+    struct literal_pattern *ways_to_write_literals;  list of ways to write this
+    int named_values_created_with_assertions;  such as "Train Arrival is a scene."
+    struct table *named_values_created_with_table;  alternatively...
+    int next_free_value;  to make distinguishable instances of this kind
+    int constant_compilation_method;  one of the *_CCM values
+
+     E: knowledge about values of this kind
+    struct inference_subject *dt_knowledge;  inferences about properties
+    struct text_stream *default_value;  used for built-in types only
+
+     F: behaviour as a property as well
+    int can_coincide_with_property;  allowed to coincide in name with a property
+    struct property *coinciding_property;  property of the same name, if any
+
+     G: performing arithmetic
+    struct text_stream *comparison_routine;  for instance, when sorting table or list entries
+    struct dimensional_rules dim_rules;  how arithmetic operations work here
+    struct unit_sequence dimensional_form;  dimensions of this kind
+    int dimensional_form_fixed;  whether they are derived
+
+     H: representing this kind at run-time
+    int weak_kind_ID;  as used at run-time by the I6 template
+    struct text_stream *name_in_template_code;  an I6 identifier
+    int class_number;  for classes of object
+    #ifdef CORE_MODULE
+    struct inter_name *con_iname;
+    struct package_request *kc_package;
+    #endif
+    int small_block_size;  if stored as a block value, size in words of the SB
+
+     I: storing values at run-time
+    int multiple_block;  TRUE for flexible-size values stored on the heap
+    int heap_size_estimate;  typical number of bytes used
+    int can_exchange;  with external files and therefore other story files
+    struct text_stream *distinguisher;  I6 routine to see if values distinguishable
+    struct kind_constructor_comparison_schema *first_comparison_schema;  list of these
+    struct text_stream *loop_domain_schema;  how to compile an I6 loop over the instances
+
+     J: printing and parsing values at run-time
+    #ifdef BYTECODE_MODULE
+    struct inter_name *kind_GPR_iname;
+    struct inter_name *instance_GPR_iname;
+    struct inter_name *first_instance_iname;
+    struct inter_name *next_instance_iname;
+    struct inter_name *pr_iname;
+    struct inter_name *inc_iname;
+    struct inter_name *dec_iname;
+    struct inter_name *ranger_iname;
+    struct inter_name *trace_iname;
+    #endif
+    struct text_stream *dt_I6_identifier;  an I6 identifier used for compiling printing rules
+    struct text_stream *name_of_printing_rule_ACTIONS;  ditto but for ACTIONS testing command
+    struct grammar_verb *understand_as_values;  used when parsing such values
+    int has_i6_GPR;  a general parsing routine exists in the I6 code for this
+    int I6_GPR_needed;  and is actually required
+    struct text_stream *explicit_i6_GPR;  routine name, when not compiled automatically
+    struct text_stream *recognition_only_GPR;  for recognising an explicit value as preposition
+
+     K: indexing and documentation
+    struct text_stream *specification_text;  text for parse_node
+    struct text_stream *constructor_description;  text used in index pages
+    struct text_stream *index_default_value;  and its description in the Kinds index
+    struct text_stream *index_maximum_value;  ditto
+    struct text_stream *index_minimum_value;  ditto
+    int index_priority;  from 1 (highest) to LOWEST_INDEX_PRIORITY (lowest)
+    int linguistic;  divide off as having linguistics content
+    int indexed_grey_if_empty;  shaded grey in the Kinds index
+    struct text_stream *documentation_reference;  documentation symbol, if any
+
+    CLASS_DEFINITION
+} kind_constructor;
+
+ +

§3. The "tupling" of an argument is the extent to which an argument can be +allowed to hold a variable-length list of kinds, rather than a single one. +There aren't actually many possibilities. +

+ +
define NO_TUPLING 0  a single kind
+define ALLOW_NOTHING_TUPLING 1  a single kind, or "nothing"
+define ARBITRARY_TUPLING 10000  a list of kinds of any length
+
+

§4. Constant compilation modes are: +

+ +
define NONE_CCM 1  constant values of this kind cannot exist
+define LITERAL_CCM 2  a numerical annotation decides the value
+define NAMED_CONSTANT_CCM 3  an instance annotation decides the value
+define SPECIAL_CCM 4  special code specific to the kind of value is needed
+
+

§5. We keep track of the newest-created base kind of value (which isn't a kind +of object) here: +

+ +
+int next_free_data_type_ID = 2;  i.e., leaving room for UNKNOWN_TY to be 1 at run-time
+kind *latest_base_kind_of_value = NULL;
+
+

§6. Creation. Constructors come from two sources. Built-in ones like "number" or +"list of K" mainly come from the commands given in the "Load-Core.i6t" +template file, which consists almost entirely of commands for the kind +interpreter, which sets up most of the above. (Similar files for other +language plugins add the remainder.) Thus a great deal can be changed about +the interplay of kinds without altering the compiler itself. +

+ +

New kinds created by the source text, by sentences like "Air pressure is a +kind of value", are always base constructors (i.e., they have arity 0); at +present there's no way to create new kinds of kinds, or new constructors, in +Inform source text. (So an extension wanting to make new constructors, say +to add new "collection classes" to Inform, will have to get its hands +dirty with Inform 6 insertions and use of the kind interpreter.) +

+ +

Here super will be the super-constructor, the one which this will construct +subkinds of. In practice this will be NULL when CON_VALUE is created, and +then CON_VALUE for kinds like "number" or this one: +

+ +
+

Weight is a kind of value.

+
+ +

but will be the constructor for "door" for kinds like this one: +

+ +
+

Portal is a kind of door.

+
+ +
+kind_constructor *Kinds::Constructors::new(parse_node_tree *T, kind_constructor *super, text_stream *source_name,
+    text_stream *initialisation_macro) {
+    kind_constructor *con = CREATE(kind_constructor);
+    kind_constructor **pC = FamiliarKinds::known_con(source_name);
+    if (pC) *pC = con;
+
+    if (super == Kinds::get_construct(K_value)) Fill in a new constructor6.1
+    else Copy the new constructor from its superconstructor6.2;
+
+    con->name_in_template_code = Str::new();
+    #ifdef CORE_MODULE
+    con->con_iname = NULL;
+    con->kc_package = NULL;
+    #endif
+    if (Str::len(source_name) > 0) WRITE_TO(con->name_in_template_code, "%S", source_name);
+    #ifdef CORE_MODULE
+    if ((con != CON_KIND_VARIABLE) && (con != CON_INTERMEDIATE))
+        con->dt_knowledge = Kinds::Knowledge::create_for_constructor(con);
+    #endif
+    con->where_defined_in_source_text = current_sentence;
+
+    kind **pK = FamiliarKinds::known_kind(source_name);
+    if (pK) *pK = Kinds::base_construction(con);
+    return con;
+}
+
+

§6.1. If our new constructor is wholly new, and isn't a subkind of something else, +we need to initialise the entire data structure; but note that, having done so, +we ask the kind interpreter to load it up with any defaults set in the +I6 template files. +

+ +

Fill in a new constructor6.1 = +

+ +
+    con->dt_tag = NULL;
+    con->group = 0;  which is invalid, so the interpreter needs to set it
+
+     A: how this came into being
+    con->defined_in_source_text = FALSE;
+    con->is_incompletely_defined = FALSE;
+    con->where_defined_in_source_text = NULL;  but will be filled in imminently
+    con->stored_as = NULL;
+
+     B: constructing kinds
+    con->constructor_arity = 0;  by default a base constructor
+    int i;
+    for (i=0; i<MAX_KIND_CONSTRUCTION_ARITY; i++) {
+        con->variance[i] = COVARIANT;
+        con->tupling[i] = NO_TUPLING;
+    }
+    con->cached_kind = NULL;
+
+     C: compatibility with other kinds
+    con->superkind_set_at = NULL;
+    con->first_casting_rule = NULL;
+    con->first_instance_rule = NULL;
+
+     D: how constant values of this kind are expressed
+    con->ways_to_write_literals = NULL;
+    con->named_values_created_with_assertions = FALSE;
+    con->named_values_created_with_table = NULL;
+    con->next_free_value = 1;
+    con->constant_compilation_method = NONE_CCM;
+
+     E: knowledge about values of this kind
+    con->dt_knowledge = NULL;  but will be filled in imminently, in almost all cases
+    con->default_value = Str::new();
+
+     F: behaviour as a property as well
+    con->can_coincide_with_property = FALSE;
+    con->coinciding_property = NULL;
+
+     G: performing arithmetic
+    con->comparison_routine = Str::new();
+    WRITE_TO(con->comparison_routine, "UnsignedCompare");
+    if ((con == CON_KIND_VARIABLE) || (con == CON_INTERMEDIATE) ||
+        ((Str::eq_wide_string(source_name, L"NUMBER_TY")) ||
+            (Str::eq_wide_string(source_name, L"REAL_NUMBER_TY"))))
+        con->dimensional_form =
+            Kinds::Dimensions::fundamental_unit_sequence(NULL);
+    else
+        con->dimensional_form =
+            Kinds::Dimensions::fundamental_unit_sequence(Kinds::base_construction(con));
+    con->dimensional_form_fixed = FALSE;
+    Kinds::Dimensions::dim_initialise(&(con->dim_rules));
+
+     H: representing this kind at run-time
+    con->weak_kind_ID = next_free_data_type_ID++;
+    con->name_in_template_code = Str::new();
+    con->class_number = 0;
+
+     I: storing values at run-time
+    con->multiple_block = FALSE;
+    con->small_block_size = 1;
+    con->heap_size_estimate = 0;
+    con->can_exchange = FALSE;
+    con->first_comparison_schema = NULL;
+    con->distinguisher = NULL;
+    con->loop_domain_schema = NULL;
+
+     J: printing and parsing values at run-time
+    con->dt_I6_identifier = Str::new();
+    con->name_of_printing_rule_ACTIONS = Str::new();
+    #ifdef BYTECODE_MODULE
+    con->kind_GPR_iname = NULL;
+    con->instance_GPR_iname = NULL;
+    con->first_instance_iname = NULL;
+    con->next_instance_iname = NULL;
+    con->pr_iname = NULL;
+    con->inc_iname = NULL;
+    con->dec_iname = NULL;
+    con->ranger_iname = NULL;
+    con->trace_iname = NULL;
+    if (Str::len(source_name) == 0) {
+        package_request *R = Kinds::Constructors::package(con);
+        con->pr_iname = Hierarchy::make_iname_in(PRINT_DASH_FN_HL, R);
+        con->trace_iname = con->pr_iname;
+    }
+    #endif
+
+    con->understand_as_values = NULL;
+    con->has_i6_GPR = FALSE;
+    con->I6_GPR_needed = FALSE;
+    con->explicit_i6_GPR = NULL;
+    con->recognition_only_GPR = NULL;
+
+     K: indexing and documentation
+    con->specification_text = NULL;
+    con->constructor_description = NULL;
+    con->index_default_value = I"--";
+    con->index_maximum_value = I"--";
+    con->index_minimum_value = I"--";
+    con->index_priority = LOWEST_INDEX_PRIORITY;
+    con->linguistic = FALSE;
+    con->indexed_grey_if_empty = FALSE;
+    con->documentation_reference = NULL;
+
+    KindCommands::play_back_kind_macro(T,
+        KindCommands::parse_kind_macro_name(I"#DEFAULTS"), con);
+    if (Str::len(initialisation_macro) > 0)
+        KindCommands::play_back_kind_macro(T,
+            KindCommands::parse_kind_macro_name(initialisation_macro), con);
+
+ +

§6.2. However, if we create our constructor as a subkind, like so: +

+ +
+

A turtle is a kind of animal.

+
+ +

then we copy the entire "animal" constructor to initialise the "turtle" one. +

+ +

Note that the weak ID number is one of the things copied; this is deliberate. +It means that all kinds of object share the same weak ID as "object". +

+ +

Copy the new constructor from its superconstructor6.2 = +

+ +
+    int I = con->allocation_id;
+    void *N = con->next_structure;
+    void *P = con->prev_structure;
+    *con = *super;
+    con->allocation_id = I;
+    con->next_structure = N;
+    con->prev_structure = P;
+    con->cached_kind = NULL;  otherwise the superkind's cache is used by mistake
+    con->name_in_template_code = Str::new();  otherwise this will be called OBJECT_TY by mistake
+
+ +

§7. The noun. It's a requirement that the following be called soon after the creation +of the constructor: +

+ +
+void Kinds::Constructors::attach_noun(kind_constructor *con, noun *nt) {
+    if ((con == NULL) || (nt == NULL)) internal_error("bad noun attachment");
+    con->dt_tag = nt;
+}
+
+wording Kinds::Constructors::get_name(kind_constructor *con, int plural_form) {
+    if (con->dt_tag) {
+        noun *nt = con->dt_tag;
+        if (nt) return Nouns::nominative(nt, plural_form);
+    }
+    return EMPTY_WORDING;
+}
+
+wording Kinds::Constructors::get_name_in_play(kind_constructor *con, int plural_form,
+    NATURAL_LANGUAGE_WORDS_TYPE *nl) {
+    if (con->dt_tag) {
+        noun *nt = con->dt_tag;
+        if (nt) return Nouns::nominative_in_language(nt, plural_form, nl);
+    }
+    return EMPTY_WORDING;
+}
+
+noun *Kinds::Constructors::get_noun(kind_constructor *con) {
+    if (con == NULL) return NULL;
+    return con->dt_tag;
+}
+
+

§8. Names in the I6 template. An identifier like WHATEVER_TY, then, begins life in a definition inside an +I6 template file; becomes attached to a constructor here; and finally winds up +back in I6 code, because we define it as the constant for the weak kind ID +of the kind which the constructor makes: +

+ +
+#ifdef CORE_MODULE
+void Kinds::Constructors::compile_I6_constants(void) {
+    kind_constructor *con;
+    LOOP_OVER(con, kind_constructor) {
+        text_stream *tn = Kinds::Constructors::name_in_template_code(con);
+        if (Str::len(tn) > 0) {
+            con->con_iname = Hierarchy::make_iname_with_specific_name(WEAK_ID_HL, tn, Kinds::Constructors::package(con));
+            Hierarchy::make_available(Emit::tree(), con->con_iname);
+            Emit::named_numeric_constant(con->con_iname, (inter_ti) con->weak_kind_ID);
+        }
+    }
+
+    inter_name *hwm = Hierarchy::find(BASE_KIND_HWM_HL);
+    Emit::named_numeric_constant(hwm, (inter_ti) next_free_data_type_ID);
+    Hierarchy::make_available(Emit::tree(), hwm);
+}
+inter_name *Kinds::Constructors::UNKNOWN_iname(void) {
+    return CON_UNKNOWN->con_iname;
+}
+package_request *Kinds::Constructors::package(kind_constructor *con) {
+    if (con->kc_package == NULL) {
+        if (con->defined_in_source_text) {
+            compilation_unit *C = CompilationUnits::find(con->where_defined_in_source_text);
+            con->kc_package = Hierarchy::package(C, KIND_HAP);
+        } else if (con->superkind_set_at) {
+            compilation_unit *C = CompilationUnits::find(con->superkind_set_at);
+            con->kc_package = Hierarchy::package(C, KIND_HAP);
+        } else {
+            con->kc_package = Hierarchy::synoptic_package(KIND_HAP);
+        }
+        wording W = Kinds::Constructors::get_name(con, FALSE);
+        if (Wordings::nonempty(W))
+            Hierarchy::markup_wording(con->kc_package, KIND_NAME_HMD, W);
+        else if (Str::len(con->name_in_template_code) > 0)
+            Hierarchy::markup(con->kc_package, KIND_NAME_HMD, con->name_in_template_code);
+        else
+            Hierarchy::markup(con->kc_package, KIND_NAME_HMD, I"(anonymous kind)");
+    }
+    return con->kc_package;
+}
+inter_name *Kinds::Constructors::iname(kind_constructor *con) {
+    return con->con_iname;
+}
+inter_name *Kinds::Constructors::first_instance_iname(kind_constructor *con) {
+    return con->first_instance_iname;
+}
+void Kinds::Constructors::set_first_instance_iname(kind_constructor *con, inter_name *iname) {
+    con->first_instance_iname = iname;
+}
+inter_name *Kinds::Constructors::next_instance_iname(kind_constructor *con) {
+    return con->next_instance_iname;
+}
+void Kinds::Constructors::set_next_instance_iname(kind_constructor *con, inter_name *iname) {
+    con->next_instance_iname = iname;
+}
+#endif
+
+text_stream *Kinds::Constructors::name_in_template_code(kind_constructor *con) {
+    return con->name_in_template_code;
+}
+
+

§9. We also need to parse this, occasionally (if we needed this more than a +small and bounded number of times we'd want a faster method, but we don't): +

+ +
+kind_constructor *Kinds::Constructors::parse(text_stream *sn) {
+    if (sn == NULL) return NULL;
+    kind_constructor *con;
+    LOOP_OVER(con, kind_constructor)
+        if (Str::eq(sn, con->name_in_template_code))
+            return con;
+    return NULL;
+}
+
+

§10. Transformations. Conversions of an existing constructor to make it a unit or enumeration also +require running macros in the kind interpreter: +

+ +
+int Kinds::Constructors::convert_to_unit(parse_node_tree *T, kind_constructor *con) {
+    if (con->is_incompletely_defined == TRUE) {
+        KindCommands::play_back_kind_macro(T,
+            KindCommands::parse_kind_macro_name(I"#UNIT"), con);
+        return TRUE;
+    }
+    if (Kinds::Constructors::is_arithmetic(con)) return TRUE;  i.e., if it succeeded
+    return FALSE;
+}
+
+int Kinds::Constructors::convert_to_enumeration(parse_node_tree *T, kind_constructor *con) {
+    if (con->is_incompletely_defined == TRUE) {
+        KindCommands::play_back_kind_macro(T,
+            KindCommands::parse_kind_macro_name(I"#ENUMERATION"), con);
+        if (con->linguistic)
+            KindCommands::play_back_kind_macro(T,
+                KindCommands::parse_kind_macro_name(I"#LINGUISTIC"), con);
+        return TRUE;
+    }
+    if (Kinds::Constructors::is_enumeration(con)) return TRUE;  i.e., if it succeeded
+    return FALSE;
+}
+
+

§11. And similarly: +

+ +
+void Kinds::Constructors::convert_to_real(parse_node_tree *T, kind_constructor *con) {
+    KindCommands::play_back_kind_macro(T,
+        KindCommands::parse_kind_macro_name(I"#REAL"), con);
+}
+
+

§12. A few base kinds are marked as "linguistic", which simply enables us to fence +them tidily off in the index. +

+ +
+void Kinds::Constructors::mark_as_linguistic(kind_constructor *con) {
+    con->linguistic = TRUE;
+}
+
+

§13. For construction purposes.

+ +
+kind **Kinds::Constructors::cache_location(kind_constructor *con) {
+    if (con) return &(con->cached_kind);
+    return NULL;
+}
+
+int Kinds::Constructors::arity(kind_constructor *con) {
+    if (con == NULL) return 0;
+    if (con->group == PROPER_CONSTRUCTOR_GRP) return con->constructor_arity;
+    return 0;
+}
+
+int Kinds::Constructors::tupling(kind_constructor *con, int b) {
+    return con->tupling[b];
+}
+
+int Kinds::Constructors::variance(kind_constructor *con, int b) {
+    return con->variance[b];
+}
+
+

§14. Questions about constructors. The rest of Inform is not encouraged to poke at constructors directly; it +ought to ask questions about kinds instead (see "Using Kinds"). However: +

+ +
+int Kinds::Constructors::is_definite(kind_constructor *con) {
+    if ((con->group == BASE_CONSTRUCTOR_GRP) ||
+        (con->group == PROPER_CONSTRUCTOR_GRP))
+            return TRUE;
+    return FALSE;
+}
+
+int Kinds::Constructors::get_weak_ID(kind_constructor *con) {
+    if (con == NULL) return 0;
+    return con->weak_kind_ID;
+}
+
+int Kinds::Constructors::is_arithmetic(kind_constructor *con) {
+    if (con == NULL) return FALSE;
+    if ((Kinds::Constructors::is_definite(con)) &&
+        (Kinds::Constructors::compatible(con,
+            Kinds::get_construct(K_arithmetic_value), FALSE))) return TRUE;
+    return FALSE;
+}
+
+int Kinds::Constructors::is_arithmetic_and_real(kind_constructor *con) {
+    if (con == NULL) return FALSE;
+    if ((Kinds::Constructors::is_definite(con)) &&
+        (Kinds::Constructors::compatible(con,
+            Kinds::get_construct(K_real_arithmetic_value), FALSE))) return TRUE;
+    return FALSE;
+}
+
+int Kinds::Constructors::is_enumeration(kind_constructor *con) {
+    if (con == NULL) return FALSE;
+    if ((Kinds::Constructors::is_definite(con)) &&
+        (Kinds::Constructors::compatible(con,
+            Kinds::get_construct(K_enumerated_value), FALSE))) return TRUE;
+    return FALSE;
+}
+
+

§15. Cast and instance lists. Each constructor has a list of other constructors (all of the KIND_OF_KIND_GRP +group) which it's an instance of: value, word value, arithmetic value, and so on. +

+ +
+int Kinds::Constructors::find_cast(kind_constructor *from, kind_constructor *to) {
+    if (to) {
+        kind_constructor_casting_rule *dtcr;
+        for (dtcr = to->first_casting_rule; dtcr; dtcr = dtcr->next_casting_rule) {
+            if (Str::len(dtcr->cast_from_kind_unparsed) > 0) {
+                dtcr->cast_from_kind =
+                    Kinds::Constructors::parse(dtcr->cast_from_kind_unparsed);
+                Str::clear(dtcr->cast_from_kind_unparsed);
+            }
+            if (from == dtcr->cast_from_kind)
+                return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+

§16. Each constructor has a list of other constructors (all of the BASE_CONSTRUCTOR_GRP +group or PROPER_CONSTRUCTOR_GRP) which it can cast to. +

+ +
+int Kinds::Constructors::find_instance(kind_constructor *from, kind_constructor *to) {
+    kind_constructor_instance *dti;
+    for (dti = from->first_instance_rule; dti; dti = dti->next_instance_rule) {
+        if (Str::len(dti->instance_of_this_unparsed) > 0) {
+            dti->instance_of_this =
+                Kinds::Constructors::parse(dti->instance_of_this_unparsed);
+            Str::clear(dti->instance_of_this_unparsed);
+        }
+        if (dti->instance_of_this == to) return TRUE;
+    }
+    return FALSE;
+}
+
+

§17. Compatibility. The following tests if from is compatible with to. +

+ +
+int Kinds::Constructors::compatible(kind_constructor *from, kind_constructor *to, int allow_casts) {
+    if (to == from) return TRUE;
+    if ((to == NULL) || (from == NULL)) return FALSE;
+    if ((allow_casts) && (Kinds::Constructors::find_cast(from, to))) return TRUE;
+    if (Kinds::Constructors::find_instance(from, to)) return TRUE;
+    return FALSE;
+}
+
+

§18. And more elaborately: +

+ +
+int Kinds::Constructors::uses_pointer_values(kind_constructor *con) {
+    if (con == NULL) return FALSE;
+    if ((Kinds::Constructors::is_definite(con)) &&
+        (Kinds::Constructors::compatible(con, Kinds::get_construct(K_pointer_value), FALSE))) return TRUE;
+    return FALSE;
+}
+
+int Kinds::Constructors::allow_word_as_pointer(kind_constructor *left, kind_constructor *right) {
+    if (Kinds::Constructors::uses_pointer_values(left) == FALSE) return FALSE;
+    if (Kinds::Constructors::uses_pointer_values(right) == TRUE) return FALSE;
+    if (Kinds::Constructors::compatible(right, left, TRUE)) return TRUE;
+    return FALSE;
+}
+
+ + +