]> ocean-lang.org Git - ocean/blobdiff - csrc/oceani.mdc
oceani: remove the HACK concerning type names starting with space
[ocean] / csrc / oceani.mdc
index 5a728b6086463bf76cadcc0d52ffc6f7be90b93c..e7976cf086ef03b338527549a4d3d069af7afd45 100644 (file)
@@ -242,9 +242,11 @@ structures can be used.
 
                parse_oceani(ss->code, &context.config, dotrace ? stderr : NULL);
 
+               resolve_consts(&context);
+               prepare_types(&context);
                if (!context.parse_error && !analyse_funcs(&context)) {
                        fprintf(stderr, "oceani: type error in program - not running.\n");
-                       context.parse_error = 1;
+                       context.parse_error += 1;
                }
 
                if (doprint) {
@@ -265,6 +267,7 @@ structures can be used.
                while (context.scope_depth > 0)
                        scope_pop(&context);
                ## free global vars
+               ## free const decls
                ## free context types
                ## free context storage
                exit(context.parse_error ? 1 : 0);
@@ -364,6 +367,7 @@ context so indicate that parsing failed.
        static void type_err(struct parse_context *c,
                             char *fmt, struct exec *loc,
                             struct type *t1, int rules, struct type *t2);
+       static void tok_err(struct parse_context *c, char *fmt, struct token *t);
 
 ###### core functions
 
@@ -392,14 +396,14 @@ context so indicate that parsing failed.
                        }
                }
                fputs("\n", stderr);
-               c->parse_error = 1;
+               c->parse_error += 1;
        }
 
        static void tok_err(struct parse_context *c, char *fmt, struct token *t)
        {
                fprintf(stderr, "%s:%d:%d: %s: %.*s\n", c->file_name, t->line, t->col, fmt,
                        t->txt.len, t->txt.txt);
-               c->parse_error = 1;
+               c->parse_error += 1;
        }
 
 ## Entities: declared and predeclared.
@@ -580,15 +584,20 @@ program and looking for errors.
 
 So `propagate_types` is passed an expected type (being a `struct type`
 pointer together with some `val_rules` flags) that the `exec` is
-expected to return, and returns the type that it does return, either
-of which can be `NULL` signifying "unknown".  An `ok` flag is passed
-by reference. It is set to `0` when an error is found, and `2` when
-any change is made.  If it remains unchanged at `1`, then no more
-propagation is needed.
+expected to return, and returns the type that it does return, either of
+which can be `NULL` signifying "unknown".  A `prop_err` flag set is
+passed by reference.  It has `Efail` set when an error is found, and
+`Eretry` when the type for some element is set via propagation.  If
+any expression cannot be evaluated immediately, `Enoconst` is set.
+If the expression can be copied, `Emaycopy` is set.
+
+If it remains unchanged at `0`, then no more propagation is needed.
 
 ###### ast
 
        enum val_rules {Rnolabel = 1<<0, Rboolok = 1<<1, Rnoconstant = 1<<2};
+       enum prop_err {Efail = 1<<0, Eretry = 1<<1, Enoconst = 1<<2,
+                      Emaycopy = 1<<3};
 
 ###### format cases
        case 'r':
@@ -597,11 +606,11 @@ propagation is needed.
                break;
 
 ###### forward decls
-       static struct type *propagate_types(struct exec *prog, struct parse_context *c, int *ok,
+       static struct type *propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
                                            struct type *type, int rules);
 ###### core functions
 
-       static struct type *__propagate_types(struct exec *prog, struct parse_context *c, int *ok,
+       static struct type *__propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
                                              struct type *type, int rules)
        {
                struct type *t;
@@ -623,13 +632,14 @@ propagation is needed.
                return Tnone;
        }
 
-       static struct type *propagate_types(struct exec *prog, struct parse_context *c, int *ok,
+       static struct type *propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
                                            struct type *type, int rules)
        {
-               struct type *ret = __propagate_types(prog, c, ok, type, rules);
+               int pre_err = c->parse_error;
+               struct type *ret = __propagate_types(prog, c, perr, type, rules);
 
-               if (c->parse_error)
-                       *ok = 0;
+               if (c->parse_error > pre_err)
+                       *perr |= Efail;
                return ret;
        }
 
@@ -647,6 +657,9 @@ the location of a value, which can be updated, in `lval`.  Others will
 set `lval` to NULL indicating that there is a value of appropriate type
 in `rval`.
 
+###### forward decls
+       static struct value interp_exec(struct parse_context *c, struct exec *e,
+                                       struct type **typeret);
 ###### core functions
 
        struct lrval {
@@ -779,10 +792,11 @@ which might be reported in error messages.
        struct type {
                struct text name;
                struct type *next;
+               struct token first_use;
                int size, align;
                int anon;
                void (*init)(struct type *type, struct value *val);
-               void (*prepare_type)(struct parse_context *c, struct type *type, int parse_time);
+               int (*prepare_type)(struct parse_context *c, struct type *type, int parse_time);
                void (*print)(struct type *type, struct value *val, FILE *f);
                void (*print_type)(struct type *type, FILE *f);
                int (*cmp_order)(struct type *t1, struct type *t2,
@@ -826,7 +840,10 @@ which might be reported in error messages.
                struct type *n;
 
                n = calloc(1, sizeof(*n));
-               *n = *proto;
+               if (proto)
+                       *n = *proto;
+               else
+                       n->size = -1;
                n->name = s;
                n->anon = anon;
                n->next = c->typelist;
@@ -911,6 +928,40 @@ which might be reported in error messages.
                        fprintf(f, "*Unknown*");                // NOTEST
        }
 
+       static void prepare_types(struct parse_context *c)
+       {
+               struct type *t;
+               int retry = 1;
+               enum { none, some, cannot } progress = none;
+
+               while (retry) {
+                       retry = 0;
+
+                       for (t = c->typelist; t; t = t->next) {
+                               if (t->size < 0)
+                                       tok_err(c, "error: type used but not declared",
+                                                &t->first_use);
+                               if (t->size == 0 && t->prepare_type) {
+                                       if (t->prepare_type(c, t, 1))
+                                               progress = some;
+                                       else if (progress == cannot)
+                                               tok_err(c, "error: type has recursive definition",
+                                                       &t->first_use);
+                                       else
+                                               retry = 1;
+                               }
+                       }
+                       switch (progress) {
+                       case cannot:
+                               retry = 0; break;
+                       case none:
+                               progress = cannot; break;
+                       case some:
+                               progress = none; break;
+                       }
+               }
+       }
+
 ###### forward decls
 
        static void free_value(struct type *type, struct value *v);
@@ -947,12 +998,10 @@ symbol for those.
 
        $*type
        Type -> IDENTIFIER ${
-               $0 = find_type(c, $1.txt);
+               $0 = find_type(c, $ID.txt);
                if (!$0) {
-                       tok_err(c,
-                               "error: undefined type", &$1);
-
-                       $0 = Tnone;
+                       $0 = add_type(c, $ID.txt, NULL);
+                       $0->first_use = $ID;
                }
        }$
        ## type grammar
@@ -1110,7 +1159,7 @@ A separate function encoding these cases will simplify some code later.
                        mpf_t fl;
                        mpf_init2(fl, 20);
                        mpf_set_q(fl, v->num);
-                       gmp_fprintf(f, "%Fg", fl);
+                       gmp_fprintf(f, "%.10Fg", fl);
                        mpf_clear(fl);
                        break;
                        }
@@ -1185,44 +1234,38 @@ executable.
 
        $*val
        Value ->  True ${
-                       $0 = new_val(Tbool, $1);
-                       $0->val.bool = 1;
-                       }$
-               | False ${
-                       $0 = new_val(Tbool, $1);
-                       $0->val.bool = 0;
-                       }$
-               | NUMBER ${
-                       $0 = new_val(Tnum, $1);
-                       {
-                       char tail[3];
-                       if (number_parse($0->val.num, tail, $1.txt) == 0)
-                               mpq_init($0->val.num);  // UNTESTED
-                               if (tail[0])
-                                       tok_err(c, "error: unsupported number suffix",
-                                               &$1);
-                       }
-                       }$
-               | STRING ${
-                       $0 = new_val(Tstr, $1);
-                       {
-                       char tail[3];
-                       string_parse(&$1, '\\', &$0->val.str, tail);
-                       if (tail[0])
-                               tok_err(c, "error: unsupported string suffix",
-                                       &$1);
-                       }
-                       }$
-               | MULTI_STRING ${
-                       $0 = new_val(Tstr, $1);
-                       {
-                       char tail[3];
-                       string_parse(&$1, '\\', &$0->val.str, tail);
+               $0 = new_val(Tbool, $1);
+               $0->val.bool = 1;
+       }$
+       | False ${
+               $0 = new_val(Tbool, $1);
+               $0->val.bool = 0;
+       }$
+       | NUMBER ${ {
+               char tail[3];
+               $0 = new_val(Tnum, $1);
+               if (number_parse($0->val.num, tail, $1.txt) == 0)
+                       mpq_init($0->val.num);  // UNTESTED
                        if (tail[0])
-                               tok_err(c, "error: unsupported string suffix",
+                               tok_err(c, "error: unsupported number suffix",
                                        &$1);
-                       }
-                       }$
+       } }$
+       | STRING ${ {
+               char tail[3];
+               $0 = new_val(Tstr, $1);
+               string_parse(&$1, '\\', &$0->val.str, tail);
+               if (tail[0])
+                       tok_err(c, "error: unsupported string suffix",
+                               &$1);
+       } }$
+       | MULTI_STRING ${ {
+               char tail[3];
+               $0 = new_val(Tstr, $1);
+               string_parse(&$1, '\\', &$0->val.str, tail);
+               if (tail[0])
+                       tok_err(c, "error: unsupported string suffix",
+                               &$1);
+       } }$
 
 ###### print exec cases
        case Xval:
@@ -1230,6 +1273,7 @@ executable.
                struct val *v = cast(val, e);
                if (v->vtype == Tstr)
                        printf("\"");
+               // FIXME how to ensure numbers have same precision.
                print_value(v->vtype, &v->val, stdout);
                if (v->vtype == Tstr)
                        printf("\"");
@@ -1581,10 +1625,10 @@ need to be freed.  For this we need to be able to find it, so assume that
                while (v) {
                        struct variable *next = v->previous;
 
-                       if (v->global) {
+                       if (v->global && v->frame_pos >= 0) {
                                free_value(v->type, var_value(&context, v));
-                               if (v->depth == 0)
-                                       // This is a global constant
+                               if (v->depth == 0 && v->type->free == function_free)
+                                       // This is a function constant
                                        free_exec(v->where_decl);
                        }
                        free(v);
@@ -1851,13 +1895,17 @@ tell if it was set or not later.
        short local_size;
        void *global, *local;
 
+###### forward decls
+       static struct value *global_alloc(struct parse_context *c, struct type *t,
+                                         struct variable *v, struct value *init);
+
 ###### ast functions
 
        static struct value *var_value(struct parse_context *c, struct variable *v)
        {
                if (!v->global) {
                        if (!c->local || !v->type)
-                               return NULL;                    // NOTEST
+                               return NULL;    // UNTESTED
                        if (v->frame_pos + v->type->size > c->local_size) {
                                printf("INVALID frame_pos\n");  // NOTEST
                                exit(2);                        // NOTEST
@@ -1883,7 +1931,7 @@ tell if it was set or not later.
                        t->prepare_type(c, t, 1);       // NOTEST
 
                if (c->global_size & (t->align - 1))
-                       c->global_size = (c->global_size + t->align) & ~(t->align-1);
+                       c->global_size = (c->global_size + t->align) & ~(t->align-1);   // NOTEST
                if (!v) {
                        v = &scratch;
                        v->type = t;
@@ -1999,7 +2047,7 @@ correctly.
                                 v->where_decl, NULL, 0, NULL);
                }
        } }$
-           | IDENTIFIER :: ${ {
+       | IDENTIFIER :: ${ {
                struct variable *v = var_decl(c, $1.txt);
                $0 = new_pos(var, $1);
                $0->var = v;
@@ -2015,7 +2063,7 @@ correctly.
                                 v->where_decl, NULL, 0, NULL);
                }
        } }$
-           | IDENTIFIER : Type ${ {
+       | IDENTIFIER : Type ${ {
                struct variable *v = var_decl(c, $1.txt);
                $0 = new_pos(var, $1);
                $0->var = v;
@@ -2033,7 +2081,7 @@ correctly.
                                 v->where_decl, NULL, 0, NULL);
                }
        } }$
-           | IDENTIFIER :: Type ${ {
+       | IDENTIFIER :: Type ${ {
                struct variable *v = var_decl(c, $1.txt);
                $0 = new_pos(var, $1);
                $0->var = v;
@@ -2058,7 +2106,11 @@ correctly.
                struct variable *v = var_ref(c, $1.txt);
                $0 = new_pos(var, $1);
                if (v == NULL) {
-                       /* This might be a label - allocate a var just in case */
+                       /* This might be a global const or a label
+                        * Allocate a var with impossible type Tnone,
+                        * which will be adjusted when we find out what it is,
+                        * or will trigger an error.
+                        */
                        v = var_decl(c, $1.txt);
                        if (v) {
                                v->type = Tnone;
@@ -2067,7 +2119,7 @@ correctly.
                        }
                }
                cast(var, $0)->var = v;
-               } }$
+       } }$
 
 ###### print exec cases
        case Xvar:
@@ -2115,19 +2167,19 @@ correctly.
                        type_err(c, "error: variable used but not declared: %v",
                                 prog, NULL, 0, NULL);
                if (v->type == NULL) {
-                       if (type && *ok != 0) {
+                       if (type && !(*perr & Efail)) {
                                v->type = type;
                                v->where_set = prog;
-                               *ok = 2;
+                               *perr |= Eretry;
                        }
-                       return type;
-               }
-               if (!type_compat(type, v->type, rules)) {
+               } else if (!type_compat(type, v->type, rules)) {
                        type_err(c, "error: expected %1%r but variable '%v' is %2", prog,
                                 type, rules, v->type);
                        type_err(c, "info: this is where '%v' was set to %1", v->where_set,
                                 v->type, rules, NULL);
                }
+               if (!v->global || v->frame_pos < 0)
+                       *perr |= Enoconst;
                if (!type)
                        return v->type;
                return type;
@@ -2177,8 +2229,8 @@ simple "Value" (to be explained later).
 ###### Grammar
        $*exec
        Term ->  Value ${ $0 = $<1; }$
-               | Variable ${ $0 = $<1; }$
-               ## term grammar
+       | Variable ${ $0 = $<1; }$
+       ## term grammar
 
 Thus far the complex types we have are arrays and structs.
 
@@ -2229,25 +2281,37 @@ with a const size by whether they are prepared at parse time or not.
 
 ###### value functions
 
-       static void array_prepare_type(struct parse_context *c, struct type *type,
+       static int array_prepare_type(struct parse_context *c, struct type *type,
                                       int parse_time)
        {
                struct value *vsize;
                mpz_t q;
-               if (!type->array.vsize || type->array.static_size)
-                       return;
-
-               vsize = var_value(c, type->array.vsize);
-               mpz_init(q);
-               mpz_tdiv_q(q, mpq_numref(vsize->num), mpq_denref(vsize->num));
-               type->array.size = mpz_get_si(q);
-               mpz_clear(q);
+               if (type->array.static_size)
+                       return 1;       // UNTESTED
+               if (type->array.unspec && parse_time)
+                       return 1;       // UNTESTED
+               if (parse_time && type->array.vsize && !type->array.vsize->global)
+                       return 1;       // UNTESTED
 
-               if (parse_time) {
-                       type->array.static_size = 1;
-                       type->size = type->array.size * type->array.member->size;
-                       type->align = type->array.member->align;
+               if (type->array.vsize) {
+                       vsize = var_value(c, type->array.vsize);
+                       if (!vsize)
+                               return 1;       // UNTESTED
+                       mpz_init(q);
+                       mpz_tdiv_q(q, mpq_numref(vsize->num), mpq_denref(vsize->num));
+                       type->array.size = mpz_get_si(q);
+                       mpz_clear(q);
                }
+               if (!parse_time)
+                       return 1;
+               if (type->array.member->size <= 0)
+                       return 0;
+
+               type->array.static_size = 1;
+               type->size = type->array.size * type->array.member->size;
+               type->align = type->array.member->align;
+
+               return 1;
        }
 
        static void array_init(struct type *type, struct value *val)
@@ -2362,9 +2426,6 @@ with a const size by whether they are prepared at parse time or not.
                t->array.size = elements;
                t->array.member = $<4;
                t->array.vsize = NULL;
-               t->array.static_size = 1;
-               t->size = t->array.size * t->array.member->size;
-               t->align = t->array.member->align;
        } }$
 
        | [ IDENTIFIER ] Type ${ {
@@ -2428,8 +2489,8 @@ with a const size by whether they are prepared at parse time or not.
                /* left must be an array, right must be a number,
                 * result is the member type of the array
                 */
-               propagate_types(b->right, c, ok, Tnum, 0);
-               t = propagate_types(b->left, c, ok, NULL, rules & Rnoconstant);
+               propagate_types(b->right, c, perr, Tnum, 0);
+               t = propagate_types(b->left, c, perr, NULL, rules & Rnoconstant);
                if (!t || t->compat != array_compat) {
                        type_err(c, "error: %1 cannot be indexed", prog, t, 0, NULL);
                        return NULL;
@@ -2518,7 +2579,12 @@ function will be needed.
                        struct type *type;
                        struct value *init;
                        int offset;
-               } *fields;
+               } *fields; // This is created when field_list is analysed.
+               struct fieldlist {
+                       struct fieldlist *prev;
+                       struct field f;
+                       struct exec *init;
+               } *field_list; // This is created during parsing
        } structure;
 
 ###### type functions
@@ -2553,6 +2619,15 @@ function will be needed.
                }
        }
 
+       static void free_fieldlist(struct fieldlist *f)
+       {
+               if (!f)
+                       return;
+               free_fieldlist(f->prev);
+               free_exec(f->init);
+               free(f);
+       }
+
        static void structure_free_type(struct type *t)
        {
                int i;
@@ -2562,6 +2637,60 @@ function will be needed.
                                           t->structure.fields[i].init);
                        }
                free(t->structure.fields);
+               free_fieldlist(t->structure.field_list);
+       }
+
+       static int structure_prepare_type(struct parse_context *c,
+                                         struct type *t, int parse_time)
+       {
+               int cnt = 0;
+               struct fieldlist *f;
+
+               if (!parse_time || t->structure.fields)
+                       return 1;
+
+               for (f = t->structure.field_list; f; f=f->prev) {
+                       enum prop_err perr;
+                       cnt += 1;
+
+                       if (f->f.type->size <= 0)
+                               return 0;
+                       if (f->f.type->prepare_type)
+                               f->f.type->prepare_type(c, f->f.type, parse_time);
+
+                       if (f->init == NULL)
+                               continue;
+                       do {
+                               perr = 0;
+                               propagate_types(f->init, c, &perr, f->f.type, 0);
+                       } while (perr & Eretry);
+                       if (perr & Efail)
+                               c->parse_error += 1;    // NOTEST
+               }
+
+               t->structure.nfields = cnt;
+               t->structure.fields = calloc(cnt, sizeof(struct field));
+               f = t->structure.field_list;
+               while (cnt > 0) {
+                       int a = f->f.type->align;
+                       cnt -= 1;
+                       t->structure.fields[cnt] = f->f;
+                       if (t->size & (a-1))
+                               t->size = (t->size | (a-1)) + 1;
+                       t->structure.fields[cnt].offset = t->size;
+                       t->size += ((f->f.type->size - 1) | (a-1)) + 1;
+                       if (a > t->align)
+                               t->align = a;
+
+                       if (f->init && !c->parse_error) {
+                               struct value vl = interp_exec(c, f->init, NULL);
+                               t->structure.fields[cnt].init =
+                                       global_alloc(c, f->f.type, NULL, &vl);
+                       }
+
+                       f = f->prev;
+               }
+               return 1;
        }
 
        static struct type structure_prototype = {
@@ -2569,6 +2698,7 @@ function will be needed.
                .free = structure_free,
                .free_type = structure_free_type,
                .print_type_decl = structure_print_type,
+               .prepare_type = structure_prepare_type,
        };
 
 ###### exec type
@@ -2626,7 +2756,7 @@ function will be needed.
        case Xfieldref:
        {
                struct fieldref *f = cast(fieldref, prog);
-               struct type *st = propagate_types(f->left, c, ok, NULL, 0);
+               struct type *st = propagate_types(f->left, c, perr, NULL, 0);
 
                if (!st)
                        type_err(c, "error: unknown type for field access", f->left,    // UNTESTED
@@ -2661,100 +2791,61 @@ function will be needed.
                break;
        }
 
-###### ast
-       struct fieldlist {
-               struct fieldlist *prev;
-               struct field f;
-       };
-
-###### ast functions
-       static void free_fieldlist(struct fieldlist *f)
-       {
-               if (!f)
-                       return;
-               free_fieldlist(f->prev);
-               if (f->f.init) {
-                       free_value(f->f.type, f->f.init);       // UNTESTED
-                       free(f->f.init);        // UNTESTED
-               }
-               free(f);
-       }
-
 ###### top level grammar
        DeclareStruct -> struct IDENTIFIER FieldBlock Newlines ${ {
-                       struct type *t =
-                               add_type(c, $2.txt, &structure_prototype);
-                       int cnt = 0;
-                       struct fieldlist *f;
-
-                       for (f = $3; f; f=f->prev)
-                               cnt += 1;
-
-                       t->structure.nfields = cnt;
-                       t->structure.fields = calloc(cnt, sizeof(struct field));
-                       f = $3;
-                       while (cnt > 0) {
-                               int a = f->f.type->align;
-                               cnt -= 1;
-                               t->structure.fields[cnt] = f->f;
-                               if (t->size & (a-1))
-                                       t->size = (t->size | (a-1)) + 1;
-                               t->structure.fields[cnt].offset = t->size;
-                               t->size += ((f->f.type->size - 1) | (a-1)) + 1;
-                               if (a > t->align)
-                                       t->align = a;
-                               f->f.init = NULL;
-                               f = f->prev;
-                       }
-               } }$
+               struct type *t;
+               t = find_type(c, $ID.txt);
+               if (!t)
+                       t = add_type(c, $ID.txt, &structure_prototype);
+               else if (t->size >= 0) {
+                       tok_err(c, "error: type already declared", &$ID);
+                       tok_err(c, "info: this is location of declartion", &t->first_use);
+                       /* Create a new one - duplicate */
+                       t = add_type(c, $ID.txt, &structure_prototype);
+               } else {
+                       struct type tmp = *t;
+                       *t = structure_prototype;
+                       t->name = tmp.name;
+                       t->next = tmp.next;
+               }
+               t->structure.field_list = $<FB;
+               t->first_use = $ID;
+       } }$
 
        $*fieldlist
        FieldBlock -> { IN OptNL FieldLines OUT OptNL } ${ $0 = $<FL; }$
-               | { SimpleFieldList } ${ $0 = $<SFL; }$
-               | IN OptNL FieldLines OUT ${ $0 = $<FL; }$
-               | SimpleFieldList EOL ${ $0 = $<SFL; }$
+       | { SimpleFieldList } ${ $0 = $<SFL; }$
+       | IN OptNL FieldLines OUT ${ $0 = $<FL; }$
+       | SimpleFieldList EOL ${ $0 = $<SFL; }$
 
        FieldLines -> SimpleFieldList Newlines ${ $0 = $<SFL; }$
-               | FieldLines SimpleFieldList Newlines ${
-                       $SFL->prev = $<FL;
-                       $0 = $<SFL;
-               }$
+       | FieldLines SimpleFieldList Newlines ${
+               $SFL->prev = $<FL;
+               $0 = $<SFL;
+       }$
 
        SimpleFieldList -> Field ${ $0 = $<F; }$
-               | SimpleFieldList ; Field ${
-                       $F->prev = $<SFL;
-                       $0 = $<F;
-               }$
-               | SimpleFieldList ; ${
-                       $0 = $<SFL;
-               }$
-               | ERROR ${ tok_err(c, "Syntax error in struct field", &$1); }$
+       | SimpleFieldList ; Field ${
+               $F->prev = $<SFL;
+               $0 = $<F;
+       }$
+       | SimpleFieldList ; ${
+               $0 = $<SFL;
+       }$
+       | ERROR ${ tok_err(c, "Syntax error in struct field", &$1); }$
 
        Field -> IDENTIFIER : Type = Expression ${ {
-                       int ok;
-
-                       $0 = calloc(1, sizeof(struct fieldlist));
-                       $0->f.name = $1.txt;
-                       $0->f.type = $<3;
-                       $0->f.init = NULL;
-                       do {
-                               ok = 1;
-                               propagate_types($<5, c, &ok, $3, 0);
-                       } while (ok == 2);
-                       if (!ok)
-                               c->parse_error = 1;     // UNTESTED
-                       else {
-                               struct value vl = interp_exec(c, $5, NULL);
-                               $0->f.init = global_alloc(c, $0->f.type, NULL, &vl);
-                       }
-               } }$
-               | IDENTIFIER : Type ${
-                       $0 = calloc(1, sizeof(struct fieldlist));
-                       $0->f.name = $1.txt;
-                       $0->f.type = $<3;
-                       if ($0->f.type->prepare_type)
-                               $0->f.type->prepare_type(c, $0->f.type, 1);
-               }$
+               $0 = calloc(1, sizeof(struct fieldlist));
+               $0->f.name = $ID.txt;
+               $0->f.type = $<Type;
+               $0->f.init = NULL;
+               $0->init = $<Expr;
+       } }$
+       | IDENTIFIER : Type ${
+               $0 = calloc(1, sizeof(struct fieldlist));
+               $0->f.name = $ID.txt;
+               $0->f.type = $<Type;
+       }$
 
 ###### forward decls
        static void structure_print_type(struct type *t, FILE *f);
@@ -2885,7 +2976,7 @@ further detailed when Expression Lists are introduced.
        struct exec *function;
 
 ###### type functions
-       void (*check_args)(struct parse_context *c, int *ok,
+       void (*check_args)(struct parse_context *c, enum prop_err *perr,
                           struct type *require, struct exec *args);
 
 ###### value functions
@@ -2902,7 +2993,7 @@ further detailed when Expression Lists are introduced.
                return 0;
        }
 
-       static void function_check_args(struct parse_context *c, int *ok,
+       static void function_check_args(struct parse_context *c, enum prop_err *perr,
                                        struct type *require, struct exec *args)
        {
                /* This should be 'compat', but we don't have a 'tuple' type to
@@ -2918,8 +3009,8 @@ further detailed when Expression Lists are introduced.
                                         args, NULL, 0, NULL);
                                break;
                        }
-                       *ok = 1;
-                       propagate_types(arg->left, c, ok, pv->var->type, 0);
+                       *perr = 0;
+                       propagate_types(arg->left, c, perr, pv->var->type, 0);
                        param = cast(binode, param->right);
                        arg = cast(binode, arg->right);
                }
@@ -2993,50 +3084,50 @@ further detailed when Expression Lists are introduced.
 
        $*variable
        FuncName -> IDENTIFIER ${ {
-                       struct variable *v = var_decl(c, $1.txt);
-                       struct var *e = new_pos(var, $1);
+               struct variable *v = var_decl(c, $1.txt);
+               struct var *e = new_pos(var, $1);
+               e->var = v;
+               if (v) {
+                       v->where_decl = e;
+                       $0 = v;
+               } else {
+                       v = var_ref(c, $1.txt);
                        e->var = v;
-                       if (v) {
-                               v->where_decl = e;
-                               $0 = v;
-                       } else {
-                               v = var_ref(c, $1.txt);
-                               e->var = v;
-                               type_err(c, "error: function '%v' redeclared",
-                                       e, NULL, 0, NULL);
-                               type_err(c, "info: this is where '%v' was first declared",
-                                       v->where_decl, NULL, 0, NULL);
-                               free_exec(e);
-                       }
-               } }$
+                       type_err(c, "error: function '%v' redeclared",
+                               e, NULL, 0, NULL);
+                       type_err(c, "info: this is where '%v' was first declared",
+                               v->where_decl, NULL, 0, NULL);
+                       free_exec(e);
+               }
+       } }$
 
        $*binode
        Args -> ArgsLine NEWLINE ${ $0 = $<AL; }$
-               | Args ArgsLine NEWLINE ${ {
-                       struct binode *b = $<AL;
-                       struct binode **bp = &b;
-                       while (*bp)
-                               bp = (struct binode **)&(*bp)->left;
-                       *bp = $<A;
-                       $0 = b;
-               } }$
+       | Args ArgsLine NEWLINE ${ {
+               struct binode *b = $<AL;
+               struct binode **bp = &b;
+               while (*bp)
+                       bp = (struct binode **)&(*bp)->left;
+               *bp = $<A;
+               $0 = b;
+       } }$
 
        ArgsLine -> ${ $0 = NULL; }$
-               | Varlist ${ $0 = $<1; }$
-               | Varlist ; ${ $0 = $<1; }$
+       | Varlist ${ $0 = $<1; }$
+       | Varlist ; ${ $0 = $<1; }$
 
        Varlist -> Varlist ; ArgDecl ${
-                       $0 = new(binode);
-                       $0->op = List;
-                       $0->left = $<Vl;
-                       $0->right = $<AD;
-               }$
-               | ArgDecl ${
-                       $0 = new(binode);
-                       $0->op = List;
-                       $0->left = NULL;
-                       $0->right = $<AD;
-               }$
+               $0 = new(binode);
+               $0->op = List;
+               $0->left = $<Vl;
+               $0->right = $<AD;
+       }$
+       | ArgDecl ${
+               $0 = new(binode);
+               $0->op = List;
+               $0->left = NULL;
+               $0->right = $<AD;
+       }$
 
        $*var
        ArgDecl -> IDENTIFIER : FormalType ${ {
@@ -3118,7 +3209,10 @@ it in the "SimpleStatement Grammar" which will be described later.
                                 prog, NULL, 0, NULL);
                        return NULL;
                }
-               v->var->type->check_args(c, ok, v->var->type, args);
+               *perr |= Enoconst;
+               v->var->type->check_args(c, perr, v->var->type, args);
+               if (v->var->type->function.inline_result)
+                       *perr |= Emaycopy;
                return v->var->type->function.return_type;
        }
 
@@ -3173,7 +3267,7 @@ Term - others will follow.
 
        $*exec
        Expression -> Term ${ $0 = $<Term; }$
-               ## expression grammar
+       ## expression grammar
 
 ### Expressions: Conditional
 
@@ -3231,9 +3325,9 @@ there.
                struct binode *b2 = cast(binode, b->right);
                struct type *t2;
 
-               propagate_types(b->left, c, ok, Tbool, 0);
-               t = propagate_types(b2->left, c, ok, type, Rnolabel);
-               t2 = propagate_types(b2->right, c, ok, type ?: t, Rnolabel);
+               propagate_types(b->left, c, perr, Tbool, 0);
+               t = propagate_types(b2->left, c, perr, type, Rnolabel);
+               t2 = propagate_types(b2->right, c, perr, type ?: t, Rnolabel);
                return t ?: t2;
        }
 
@@ -3288,17 +3382,17 @@ lists.  In that case a separate function is used to print them.
 
        $*binode
        ExpressionList -> ExpressionList , Expression ${
-                       $0 = new(binode);
-                       $0->op = List;
-                       $0->left = $<1;
-                       $0->right = $<3;
-               }$
-               | Expression ${
-                       $0 = new(binode);
-                       $0->op = List;
-                       $0->left = NULL;
-                       $0->right = $<1;
-               }$
+               $0 = new(binode);
+               $0->op = List;
+               $0->left = $<1;
+               $0->right = $<3;
+       }$
+       | Expression ${
+               $0 = new(binode);
+               $0->op = List;
+               $0->left = NULL;
+               $0->right = $<1;
+       }$
 
 ### Expressions: Boolean
 
@@ -3320,42 +3414,42 @@ evaluate the second expression if not necessary.
        $LEFT not
 
 ###### expression grammar
-               | Expression or Expression ${ {
-                       struct binode *b = new(binode);
-                       b->op = Or;
-                       b->left = $<1;
-                       b->right = $<3;
-                       $0 = b;
-               } }$
-               | Expression or else Expression ${ {
-                       struct binode *b = new(binode);
-                       b->op = OrElse;
-                       b->left = $<1;
-                       b->right = $<4;
-                       $0 = b;
-               } }$
+       | Expression or Expression ${ {
+               struct binode *b = new(binode);
+               b->op = Or;
+               b->left = $<1;
+               b->right = $<3;
+               $0 = b;
+       } }$
+       | Expression or else Expression ${ {
+               struct binode *b = new(binode);
+               b->op = OrElse;
+               b->left = $<1;
+               b->right = $<4;
+               $0 = b;
+       } }$
 
-               | Expression and Expression ${ {
-                       struct binode *b = new(binode);
-                       b->op = And;
-                       b->left = $<1;
-                       b->right = $<3;
-                       $0 = b;
-               } }$
-               | Expression and then Expression ${ {
-                       struct binode *b = new(binode);
-                       b->op = AndThen;
-                       b->left = $<1;
-                       b->right = $<4;
-                       $0 = b;
-               } }$
+       | Expression and Expression ${ {
+               struct binode *b = new(binode);
+               b->op = And;
+               b->left = $<1;
+               b->right = $<3;
+               $0 = b;
+       } }$
+       | Expression and then Expression ${ {
+               struct binode *b = new(binode);
+               b->op = AndThen;
+               b->left = $<1;
+               b->right = $<4;
+               $0 = b;
+       } }$
 
-               | not Expression ${ {
-                       struct binode *b = new(binode);
-                       b->op = Not;
-                       b->right = $<2;
-                       $0 = b;
-               } }$
+       | not Expression ${ {
+               struct binode *b = new(binode);
+               b->op = Not;
+               b->right = $<2;
+               $0 = b;
+       } }$
 
 ###### print binode cases
        case And:
@@ -3400,8 +3494,8 @@ evaluate the second expression if not necessary.
        case OrElse:
        case Not:
                /* both must be Tbool, result is Tbool */
-               propagate_types(b->left, c, ok, Tbool, 0);
-               propagate_types(b->right, c, ok, Tbool, 0);
+               propagate_types(b->left, c, perr, Tbool, 0);
+               propagate_types(b->right, c, perr, Tbool, 0);
                if (type && type != Tbool)
                        type_err(c, "error: %1 operation found where %2 expected", prog,
                                   Tbool, 0, type);
@@ -3477,12 +3571,12 @@ expression operator, and the `CMPop` non-terminal will match one of them.
 ###### Grammar
 
        $eop
-       CMPop ->   < ${ $0.op = Less; }$
-               |  > ${ $0.op = Gtr; }$
-               |  <= ${ $0.op = LessEq; }$
-               |  >= ${ $0.op = GtrEq; }$
-               |  == ${ $0.op = Eql; }$
-               |  != ${ $0.op = NEql; }$
+       CMPop ->  < ${ $0.op = Less; }$
+       |         > ${ $0.op = Gtr; }$
+       |         <= ${ $0.op = LessEq; }$
+       |         >= ${ $0.op = GtrEq; }$
+       |         == ${ $0.op = Eql; }$
+       |         != ${ $0.op = NEql; }$
 
 ###### print binode cases
 
@@ -3515,13 +3609,13 @@ expression operator, and the `CMPop` non-terminal will match one of them.
        case Eql:
        case NEql:
                /* Both must match but not be labels, result is Tbool */
-               t = propagate_types(b->left, c, ok, NULL, Rnolabel);
+               t = propagate_types(b->left, c, perr, NULL, Rnolabel);
                if (t)
-                       propagate_types(b->right, c, ok, t, 0);
+                       propagate_types(b->right, c, perr, t, 0);
                else {
-                       t = propagate_types(b->right, c, ok, NULL, Rnolabel);   // UNTESTED
+                       t = propagate_types(b->right, c, perr, NULL, Rnolabel); // UNTESTED
                        if (t)  // UNTESTED
-                               t = propagate_types(b->left, c, ok, t, 0);      // UNTESTED
+                               t = propagate_types(b->left, c, perr, t, 0);    // UNTESTED
                }
                if (!type_compat(type, Tbool, 0))
                        type_err(c, "error: Comparison returns %1 but %2 expected", prog,
@@ -3622,17 +3716,17 @@ parentheses around an expression converts it into a Term,
 ###### Grammar
 
        $eop
-       Eop ->    + ${ $0.op = Plus; }$
-               | - ${ $0.op = Minus; }$
+       Eop ->   + ${ $0.op = Plus; }$
+       |        - ${ $0.op = Minus; }$
 
-       Uop ->    + ${ $0.op = Absolute; }$
-               | - ${ $0.op = Negate; }$
-               | $ ${ $0.op = StringConv; }$
+       Uop ->   + ${ $0.op = Absolute; }$
+       |        - ${ $0.op = Negate; }$
+       |        $ ${ $0.op = StringConv; }$
 
-       Top ->    * ${ $0.op = Times; }$
-               | / ${ $0.op = Divide; }$
-               | % ${ $0.op = Rem; }$
-               | ++ ${ $0.op = Concat; }$
+       Top ->   * ${ $0.op = Times; }$
+       |        / ${ $0.op = Divide; }$
+       |        % ${ $0.op = Rem; }$
+       |        ++ ${ $0.op = Concat; }$
 
 ###### print binode cases
        case Plus:
@@ -3685,8 +3779,8 @@ parentheses around an expression converts it into a Term,
        case Negate:
                /* as propagate_types ignores a NULL,
                 * unary ops fit here too */
-               propagate_types(b->left, c, ok, Tnum, 0);
-               propagate_types(b->right, c, ok, Tnum, 0);
+               propagate_types(b->left, c, perr, Tnum, 0);
+               propagate_types(b->right, c, perr, Tnum, 0);
                if (!type_compat(type, Tnum, 0))
                        type_err(c, "error: Arithmetic returns %1 but %2 expected", prog,
                                   Tnum, rules, type);
@@ -3694,8 +3788,8 @@ parentheses around an expression converts it into a Term,
 
        case Concat:
                /* both must be Tstr, result is Tstr */
-               propagate_types(b->left, c, ok, Tstr, 0);
-               propagate_types(b->right, c, ok, Tstr, 0);
+               propagate_types(b->left, c, perr, Tstr, 0);
+               propagate_types(b->right, c, perr, Tstr, 0);
                if (!type_compat(type, Tstr, 0))
                        type_err(c, "error: Concat returns %1 but %2 expected", prog,
                                   Tstr, rules, type);
@@ -3703,7 +3797,7 @@ parentheses around an expression converts it into a Term,
 
        case StringConv:
                /* op must be string, result is number */
-               propagate_types(b->left, c, ok, Tstr, 0);
+               propagate_types(b->left, c, perr, Tstr, 0);
                if (!type_compat(type, Tnum, 0))
                        type_err(c,     // UNTESTED
                          "error: Can only convert string to number, not %1",
@@ -3711,7 +3805,7 @@ parentheses around an expression converts it into a Term,
                return Tnum;
 
        case Bracket:
-               return propagate_types(b->right, c, ok, type, 0);
+               return propagate_types(b->right, c, perr, type, 0);
 
 ###### interp binode cases
 
@@ -3869,78 +3963,78 @@ the common header for all reductions to use.
 
        $*binode
        Block -> { IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
-               | { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
-               | SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
-               | SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
-               | IN OptNL Statementlist OUT ${ $0 = $<Sl; }$
+       |        { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
+       |        SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
+       |        SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
+       |        IN OptNL Statementlist OUT ${ $0 = $<Sl; }$
 
        OpenBlock -> OpenScope { IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
-               | OpenScope { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
-               | OpenScope SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
-               | OpenScope SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
-               | IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
+       |        OpenScope { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
+       |        OpenScope SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
+       |        OpenScope SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
+       |        IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
 
        UseBlock -> { OpenScope IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
-               | { OpenScope SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
-               | IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
+       |        { OpenScope SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
+       |        IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
 
        ColonBlock -> { IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
-               | { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
-               | : SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
-               | : SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
-               | : IN OptNL Statementlist OUT ${ $0 = $<Sl; }$
+       |        { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
+       |        : SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
+       |        : SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
+       |        : IN OptNL Statementlist OUT ${ $0 = $<Sl; }$
 
        Statementlist -> ComplexStatements ${ $0 = reorder_bilist($<CS); }$
 
        ComplexStatements -> ComplexStatements ComplexStatement ${
-                       if ($2 == NULL) {
-                               $0 = $<1;
-                       } else {
-                               $0 = new(binode);
-                               $0->op = Block;
-                               $0->left = $<1;
-                               $0->right = $<2;
-                       }
-               }$
-               | ComplexStatement ${
-                       if ($1 == NULL) {
-                               $0 = NULL;
-                       } else {
-                               $0 = new(binode);
-                               $0->op = Block;
-                               $0->left = NULL;
-                               $0->right = $<1;
-                       }
-               }$
-
-       $*exec
-       ComplexStatement -> SimpleStatements Newlines ${
-                       $0 = reorder_bilist($<SS);
-                       }$
-               |  SimpleStatements ; Newlines ${
-                       $0 = reorder_bilist($<SS);
-                       }$
-               ## ComplexStatement Grammar
-
-       $*binode
-       SimpleStatements -> SimpleStatements ; SimpleStatement ${
+               if ($2 == NULL) {
+                       $0 = $<1;
+               } else {
                        $0 = new(binode);
                        $0->op = Block;
                        $0->left = $<1;
-                       $0->right = $<3;
-                       }$
-               | SimpleStatement ${
+                       $0->right = $<2;
+               }
+       }$
+       | ComplexStatement ${
+               if ($1 == NULL) {
+                       $0 = NULL;
+               } else {
                        $0 = new(binode);
                        $0->op = Block;
                        $0->left = NULL;
                        $0->right = $<1;
-                       }$
+               }
+       }$
+
+       $*exec
+       ComplexStatement -> SimpleStatements Newlines ${
+               $0 = reorder_bilist($<SS);
+       }$
+       |  SimpleStatements ; Newlines ${
+               $0 = reorder_bilist($<SS);
+       }$
+       ## ComplexStatement Grammar
+
+       $*binode
+       SimpleStatements -> SimpleStatements ; SimpleStatement ${
+               $0 = new(binode);
+               $0->op = Block;
+               $0->left = $<1;
+               $0->right = $<3;
+       }$
+       | SimpleStatement ${
+               $0 = new(binode);
+               $0->op = Block;
+               $0->left = NULL;
+               $0->right = $<1;
+       }$
 
        $TERM pass
        $*exec
        SimpleStatement -> pass ${ $0 = NULL; }$
-               | ERROR ${ tok_err(c, "Syntax error in statement", &$1); }$
-               ## SimpleStatement Grammar
+       | ERROR ${ tok_err(c, "Syntax error in statement", &$1); }$
+       ## SimpleStatement Grammar
 
 ###### print binode cases
        case Block:
@@ -3978,7 +4072,7 @@ the common header for all reductions to use.
                struct binode *e;
 
                for (e = b; e; e = cast(binode, e->right)) {
-                       t = propagate_types(e->left, c, ok, NULL, rules);
+                       t = propagate_types(e->left, c, perr, NULL, rules);
                        if ((rules & Rboolok) && (t == Tbool || t == Tnone))
                                t = NULL;
                        if (t == Tnone && e->right)
@@ -4067,7 +4161,7 @@ printed.
                else
                        b = cast(binode, b->right);
                while (b) {
-                       propagate_types(b->left, c, ok, NULL, Rnolabel);
+                       propagate_types(b->left, c, perr, NULL, Rnolabel);
                        b = cast(binode, b->right);
                }
                break;
@@ -4111,31 +4205,31 @@ it is declared, and error will be raised as the name is created as
 
 ###### SimpleStatement Grammar
        | Term = Expression ${
-                       $0 = b= new(binode);
-                       b->op = Assign;
-                       b->left = $<1;
-                       b->right = $<3;
-               }$
+               $0 = b= new(binode);
+               b->op = Assign;
+               b->left = $<1;
+               b->right = $<3;
+       }$
        | VariableDecl = Expression ${
-                       $0 = b= new(binode);
-                       b->op = Declare;
-                       b->left = $<1;
-                       b->right =$<3;
-               }$
+               $0 = b= new(binode);
+               b->op = Declare;
+               b->left = $<1;
+               b->right =$<3;
+       }$
 
        | VariableDecl ${
-                       if ($1->var->where_set == NULL) {
-                               type_err(c,
-                                        "Variable declared with no type or value: %v",
-                                        $1, NULL, 0, NULL);
-                               free_var($1);
-                       } else {
-                               $0 = b = new(binode);
-                               b->op = Declare;
-                               b->left = $<1;
-                               b->right = NULL;
-                       }
-               }$
+               if ($1->var->where_set == NULL) {
+                       type_err(c,
+                                "Variable declared with no type or value: %v",
+                                $1, NULL, 0, NULL);
+                       free_var($1);
+               } else {
+                       $0 = b = new(binode);
+                       b->op = Declare;
+                       b->left = $<1;
+                       b->right = NULL;
+               }
+       }$
 
 ###### print binode cases
 
@@ -4184,23 +4278,23 @@ it is declared, and error will be raised as the name is created as
                 * For Assign, left must not be constant.
                 * result is Tnone
                 */
-               t = propagate_types(b->left, c, ok, NULL,
+               t = propagate_types(b->left, c, perr, NULL,
                                    Rnolabel | (b->op == Assign ? Rnoconstant : 0));
                if (!b->right)
                        return Tnone;
 
                if (t) {
-                       if (propagate_types(b->right, c, ok, t, 0) != t)
+                       if (propagate_types(b->right, c, perr, t, 0) != t)
                                if (b->left->type == Xvar)
                                        type_err(c, "info: variable '%v' was set as %1 here.",
                                                 cast(var, b->left)->var->where_set, t, rules, NULL);
                } else {
-                       t = propagate_types(b->right, c, ok, NULL, Rnolabel);
+                       t = propagate_types(b->right, c, perr, NULL, Rnolabel);
                        if (t)
-                               propagate_types(b->left, c, ok, t,
+                               propagate_types(b->left, c, perr, t,
                                                (b->op == Assign ? Rnoconstant : 0));
                }
-               if (t && t->dup == NULL && t->name.txt[0] != ' ') // HACK
+               if (t && t->dup == NULL && !(*perr & Emaycopy))
                        type_err(c, "error: cannot assign value of type %1", b, t, 0, NULL);
                return Tnone;
 
@@ -4274,7 +4368,7 @@ function which has a return type, and the "condition" code blocks in
 
        case Use:
                /* result matches value */
-               return propagate_types(b->right, c, ok, type, 0);
+               return propagate_types(b->right, c, perr, type, 0);
 
 ###### interp binode cases
 
@@ -4420,136 +4514,136 @@ casepart` to track a list of case parts.
        // ForPart, SwitchPart, and IfPart open scopes, o we have to close
        // them.  WhilePart opens and closes its own scope.
        CondStatement -> ForPart OptNL ThenPart OptNL WhilePart CondSuffix ${
-                       $0 = $<CS;
-                       $0->forpart = $<FP;
-                       $0->thenpart = $<TP;
-                       $0->looppart = $<WP;
-                       var_block_close(c, CloseSequential, $0);
-                       }$
-               | ForPart OptNL WhilePart CondSuffix ${
-                       $0 = $<CS;
-                       $0->forpart = $<FP;
-                       $0->looppart = $<WP;
-                       var_block_close(c, CloseSequential, $0);
-                       }$
-               | WhilePart CondSuffix ${
-                       $0 = $<CS;
-                       $0->looppart = $<WP;
-                       }$
-               | SwitchPart OptNL CasePart CondSuffix ${
-                       $0 = $<CS;
-                       $0->condpart = $<SP;
-                       $CP->next = $0->casepart;
-                       $0->casepart = $<CP;
-                       var_block_close(c, CloseSequential, $0);
-                       }$
-               | SwitchPart : IN OptNL CasePart CondSuffix OUT Newlines ${
-                       $0 = $<CS;
-                       $0->condpart = $<SP;
-                       $CP->next = $0->casepart;
-                       $0->casepart = $<CP;
-                       var_block_close(c, CloseSequential, $0);
-                       }$
-               | IfPart IfSuffix ${
-                       $0 = $<IS;
-                       $0->condpart = $IP.condpart; $IP.condpart = NULL;
-                       $0->thenpart = $IP.thenpart; $IP.thenpart = NULL;
-                       // This is where we close an "if" statement
-                       var_block_close(c, CloseSequential, $0);
-                       }$
+               $0 = $<CS;
+               $0->forpart = $<FP;
+               $0->thenpart = $<TP;
+               $0->looppart = $<WP;
+               var_block_close(c, CloseSequential, $0);
+       }$
+       | ForPart OptNL WhilePart CondSuffix ${
+               $0 = $<CS;
+               $0->forpart = $<FP;
+               $0->looppart = $<WP;
+               var_block_close(c, CloseSequential, $0);
+       }$
+       | WhilePart CondSuffix ${
+               $0 = $<CS;
+               $0->looppart = $<WP;
+       }$
+       | SwitchPart OptNL CasePart CondSuffix ${
+               $0 = $<CS;
+               $0->condpart = $<SP;
+               $CP->next = $0->casepart;
+               $0->casepart = $<CP;
+               var_block_close(c, CloseSequential, $0);
+       }$
+       | SwitchPart : IN OptNL CasePart CondSuffix OUT Newlines ${
+               $0 = $<CS;
+               $0->condpart = $<SP;
+               $CP->next = $0->casepart;
+               $0->casepart = $<CP;
+               var_block_close(c, CloseSequential, $0);
+       }$
+       | IfPart IfSuffix ${
+               $0 = $<IS;
+               $0->condpart = $IP.condpart; $IP.condpart = NULL;
+               $0->thenpart = $IP.thenpart; $IP.thenpart = NULL;
+               // This is where we close an "if" statement
+               var_block_close(c, CloseSequential, $0);
+       }$
 
        CondSuffix -> IfSuffix ${
-                       $0 = $<1;
-               }$
-               | Newlines CasePart CondSuffix ${
-                       $0 = $<CS;
-                       $CP->next = $0->casepart;
-                       $0->casepart = $<CP;
-               }$
-               | CasePart CondSuffix ${
-                       $0 = $<CS;
-                       $CP->next = $0->casepart;
-                       $0->casepart = $<CP;
-               }$
+               $0 = $<1;
+       }$
+       | Newlines CasePart CondSuffix ${
+               $0 = $<CS;
+               $CP->next = $0->casepart;
+               $0->casepart = $<CP;
+       }$
+       | CasePart CondSuffix ${
+               $0 = $<CS;
+               $CP->next = $0->casepart;
+               $0->casepart = $<CP;
+       }$
 
        IfSuffix -> Newlines ${ $0 = new(cond_statement); }$
-               | Newlines ElsePart ${ $0 = $<EP; }$
-               | ElsePart ${$0 = $<EP; }$
+       | Newlines ElsePart ${ $0 = $<EP; }$
+       | ElsePart ${$0 = $<EP; }$
 
        ElsePart -> else OpenBlock Newlines ${
-                       $0 = new(cond_statement);
-                       $0->elsepart = $<OB;
-                       var_block_close(c, CloseElse, $0->elsepart);
-               }$
-               | else OpenScope CondStatement ${
-                       $0 = new(cond_statement);
-                       $0->elsepart = $<CS;
-                       var_block_close(c, CloseElse, $0->elsepart);
-               }$
+               $0 = new(cond_statement);
+               $0->elsepart = $<OB;
+               var_block_close(c, CloseElse, $0->elsepart);
+       }$
+       | else OpenScope CondStatement ${
+               $0 = new(cond_statement);
+               $0->elsepart = $<CS;
+               var_block_close(c, CloseElse, $0->elsepart);
+       }$
 
        $*casepart
        CasePart -> case Expression OpenScope ColonBlock ${
-                       $0 = calloc(1,sizeof(struct casepart));
-                       $0->value = $<Ex;
-                       $0->action = $<Bl;
-                       var_block_close(c, CloseParallel, $0->action);
-               }$
+               $0 = calloc(1,sizeof(struct casepart));
+               $0->value = $<Ex;
+               $0->action = $<Bl;
+               var_block_close(c, CloseParallel, $0->action);
+       }$
 
        $*exec
        // These scopes are closed in CondStatement
        ForPart -> for OpenBlock ${
-                       $0 = $<Bl;
-               }$
+               $0 = $<Bl;
+       }$
 
        ThenPart -> then OpenBlock ${
-                       $0 = $<OB;
-                       var_block_close(c, CloseSequential, $0);
-               }$
+               $0 = $<OB;
+               var_block_close(c, CloseSequential, $0);
+       }$
 
        $*binode
        // This scope is closed in CondStatement
        WhilePart -> while UseBlock OptNL do OpenBlock ${
-                       $0 = new(binode);
-                       $0->op = Loop;
-                       $0->left = $<UB;
-                       $0->right = $<OB;
-                       var_block_close(c, CloseSequential, $0->right);
-                       var_block_close(c, CloseSequential, $0);
-               }$
-               | while OpenScope Expression OpenScope ColonBlock ${
-                       $0 = new(binode);
-                       $0->op = Loop;
-                       $0->left = $<Exp;
-                       $0->right = $<CB;
-                       var_block_close(c, CloseSequential, $0->right);
-                       var_block_close(c, CloseSequential, $0);
-               }$
+               $0 = new(binode);
+               $0->op = Loop;
+               $0->left = $<UB;
+               $0->right = $<OB;
+               var_block_close(c, CloseSequential, $0->right);
+               var_block_close(c, CloseSequential, $0);
+       }$
+       | while OpenScope Expression OpenScope ColonBlock ${
+               $0 = new(binode);
+               $0->op = Loop;
+               $0->left = $<Exp;
+               $0->right = $<CB;
+               var_block_close(c, CloseSequential, $0->right);
+               var_block_close(c, CloseSequential, $0);
+       }$
 
        $cond_statement
        IfPart -> if UseBlock OptNL then OpenBlock ${
-                       $0.condpart = $<UB;
-                       $0.thenpart = $<OB;
-                       var_block_close(c, CloseParallel, $0.thenpart);
-               }$
-               | if OpenScope Expression OpenScope ColonBlock ${
-                       $0.condpart = $<Ex;
-                       $0.thenpart = $<CB;
-                       var_block_close(c, CloseParallel, $0.thenpart);
-               }$
-               | if OpenScope Expression OpenScope OptNL then Block ${
-                       $0.condpart = $<Ex;
-                       $0.thenpart = $<Bl;
-                       var_block_close(c, CloseParallel, $0.thenpart);
-               }$
+               $0.condpart = $<UB;
+               $0.thenpart = $<OB;
+               var_block_close(c, CloseParallel, $0.thenpart);
+       }$
+       | if OpenScope Expression OpenScope ColonBlock ${
+               $0.condpart = $<Ex;
+               $0.thenpart = $<CB;
+               var_block_close(c, CloseParallel, $0.thenpart);
+       }$
+       | if OpenScope Expression OpenScope OptNL then Block ${
+               $0.condpart = $<Ex;
+               $0.thenpart = $<Bl;
+               var_block_close(c, CloseParallel, $0.thenpart);
+       }$
 
        $*exec
        // This scope is closed in CondStatement
        SwitchPart -> switch OpenScope Expression ${
-                       $0 = $<Ex;
-               }$
-               | switch UseBlock ${
-                       $0 = $<Bl;
-               }$
+               $0 = $<Ex;
+       }$
+       | switch UseBlock ${
+               $0 = $<Bl;
+       }$
 
 ###### print binode cases
        case Loop:
@@ -4661,10 +4755,10 @@ casepart` to track a list of case parts.
 
 ###### propagate binode cases
        case Loop:
-               t = propagate_types(b->right, c, ok, Tnone, 0);
+               t = propagate_types(b->right, c, perr, Tnone, 0);
                if (!type_compat(Tnone, t, 0))
-                       *ok = 0;        // UNTESTED
-               return propagate_types(b->left, c, ok, type, rules);
+                       *perr |= Efail; // UNTESTED
+               return propagate_types(b->left, c, perr, type, rules);
 
 ###### propagate exec cases
        case Xcond_statement:
@@ -4682,51 +4776,51 @@ casepart` to track a list of case parts.
                struct cond_statement *cs = cast(cond_statement, prog);
                struct casepart *cp;
 
-               t = propagate_types(cs->forpart, c, ok, Tnone, 0);
+               t = propagate_types(cs->forpart, c, perr, Tnone, 0);
                if (!type_compat(Tnone, t, 0))
-                       *ok = 0;        // UNTESTED
+                       *perr |= Efail; // UNTESTED
 
                if (cs->looppart) {
-                       t = propagate_types(cs->thenpart, c, ok, Tnone, 0);
+                       t = propagate_types(cs->thenpart, c, perr, Tnone, 0);
                        if (!type_compat(Tnone, t, 0))
-                               *ok = 0;        // UNTESTED
+                               *perr |= Efail; // UNTESTED
                }
                if (cs->casepart == NULL) {
-                       propagate_types(cs->condpart, c, ok, Tbool, 0);
-                       propagate_types(cs->looppart, c, ok, Tbool, 0);
+                       propagate_types(cs->condpart, c, perr, Tbool, 0);
+                       propagate_types(cs->looppart, c, perr, Tbool, 0);
                } else {
                        /* Condpart must match case values, with bool permitted */
                        t = NULL;
                        for (cp = cs->casepart;
                             cp && !t; cp = cp->next)
-                               t = propagate_types(cp->value, c, ok, NULL, 0);
+                               t = propagate_types(cp->value, c, perr, NULL, 0);
                        if (!t && cs->condpart)
-                               t = propagate_types(cs->condpart, c, ok, NULL, Rboolok);        // UNTESTED
+                               t = propagate_types(cs->condpart, c, perr, NULL, Rboolok);      // UNTESTED
                        if (!t && cs->looppart)
-                               t = propagate_types(cs->looppart, c, ok, NULL, Rboolok);        // UNTESTED
+                               t = propagate_types(cs->looppart, c, perr, NULL, Rboolok);      // UNTESTED
                        // Now we have a type (I hope) push it down
                        if (t) {
                                for (cp = cs->casepart; cp; cp = cp->next)
-                                       propagate_types(cp->value, c, ok, t, 0);
-                               propagate_types(cs->condpart, c, ok, t, Rboolok);
-                               propagate_types(cs->looppart, c, ok, t, Rboolok);
+                                       propagate_types(cp->value, c, perr, t, 0);
+                               propagate_types(cs->condpart, c, perr, t, Rboolok);
+                               propagate_types(cs->looppart, c, perr, t, Rboolok);
                        }
                }
                // (if)then, else, and case parts must return expected type.
                if (!cs->looppart && !type)
-                       type = propagate_types(cs->thenpart, c, ok, NULL, rules);
+                       type = propagate_types(cs->thenpart, c, perr, NULL, rules);
                if (!type)
-                       type = propagate_types(cs->elsepart, c, ok, NULL, rules);
+                       type = propagate_types(cs->elsepart, c, perr, NULL, rules);
                for (cp = cs->casepart;
                     cp && !type;
                     cp = cp->next)     // UNTESTED
-                       type = propagate_types(cp->action, c, ok, NULL, rules); // UNTESTED
+                       type = propagate_types(cp->action, c, perr, NULL, rules);       // UNTESTED
                if (type) {
                        if (!cs->looppart)
-                               propagate_types(cs->thenpart, c, ok, type, rules);
-                       propagate_types(cs->elsepart, c, ok, type, rules);
+                               propagate_types(cs->thenpart, c, perr, type, rules);
+                       propagate_types(cs->elsepart, c, perr, type, rules);
                        for (cp = cs->casepart; cp ; cp = cp->next)
-                               propagate_types(cp->action, c, ok, type, rules);
+                               propagate_types(cp->action, c, perr, type, rules);
                        return type;
                } else
                        return NULL;
@@ -4809,20 +4903,21 @@ various declarations in the parse context.
        ## declare terminals
 
        OptNL ->
-               | OptNL NEWLINE
+       | OptNL NEWLINE
+
        Newlines -> NEWLINE
-               | Newlines NEWLINE
+       | Newlines NEWLINE
 
        DeclarationList -> Declaration
-               | DeclarationList Declaration
+       | DeclarationList Declaration
 
        Declaration -> ERROR Newlines ${
-                       tok_err(c,      // UNTESTED
-                               "error: unhandled parse error", &$1);
-               }$
-               | DeclareConstant
-               | DeclareFunction
-               | DeclareStruct
+               tok_err(c,      // UNTESTED
+                       "error: unhandled parse error", &$1);
+       }$
+       | DeclareConstant
+       | DeclareFunction
+       | DeclareStruct
 
        ## top level grammar
 
@@ -4830,13 +4925,15 @@ various declarations in the parse context.
 
 ### The `const` section
 
-As well as being defined in with the code that uses them, constants
-can be declared at the top level.  These have full-file scope, so they
-are always `InScope`.  The value of a top level constant can be given
-as an expression, and this is evaluated immediately rather than in the
-later interpretation stage.  Once we add functions to the language, we
-will need rules concern which, if any, can be used to define a top
-level constant.
+As well as being defined in with the code that uses them, constants can
+be declared at the top level.  These have full-file scope, so they are
+always `InScope`, even before(!) they have been declared.  The value of
+a top level constant can be given as an expression, and this is
+evaluated after parsing and before execution.
+
+A function call can be used to evaluate a constant, but it will not have
+access to any program state, once such statement becomes meaningful.
+e.g.  arguments and filesystem will not be visible.
 
 Constants are defined in a section that starts with the reserved word
 `const` and then has a block with a list of assignment statements.
@@ -4845,97 +4942,147 @@ make it clear that they are constants.  Type can also be given: if
 not, the type will be determined during analysis, as with other
 constants.
 
-As the types constants are inserted at the head of a list, printing
-them in the same order that they were read is not straight forward.
-We take a quadratic approach here and count the number of constants
-(variables of depth 0), then count down from there, each time
-searching through for the Nth constant for decreasing N.
+###### parse context
+       struct binode *constlist;
 
 ###### top level grammar
 
        $TERM const
 
        DeclareConstant -> const { IN OptNL ConstList OUT OptNL } Newlines
-               | const { SimpleConstList } Newlines
-               | const IN OptNL ConstList OUT Newlines
-               | const SimpleConstList Newlines
+       | const { SimpleConstList } Newlines
+       | const IN OptNL ConstList OUT Newlines
+       | const SimpleConstList Newlines
 
        ConstList -> ConstList SimpleConstLine
-               | SimpleConstLine
+       | SimpleConstLine
+
        SimpleConstList -> SimpleConstList ; Const
-               | Const
-               | SimpleConstList ;
+       | Const
+       | SimpleConstList ;
+
        SimpleConstLine -> SimpleConstList Newlines
-               | ERROR Newlines ${ tok_err(c, "Syntax error in constant", &$1); }$
+       | ERROR Newlines ${ tok_err(c, "Syntax error in constant", &$1); }$
 
        $*type
        CType -> Type   ${ $0 = $<1; }$
-               |       ${ $0 = NULL; }$
+       |               ${ $0 = NULL; }$
+
        $void
        Const -> IDENTIFIER :: CType = Expression ${ {
-               int ok;
                struct variable *v;
+               struct binode *bl, *bv;
+               struct var *var = new_pos(var, $ID);
 
-               v = var_decl(c, $1.txt);
+               v = var_decl(c, $ID.txt);
                if (v) {
-                       struct var *var = new_pos(var, $1);
                        v->where_decl = var;
                        v->where_set = var;
-                       var->var = v;
+                       v->type = $<CT;
                        v->constant = 1;
                        v->global = 1;
                } else {
-                       struct variable *vorig = var_ref(c, $1.txt);
-                       tok_err(c, "error: name already declared", &$1);
-                       type_err(c, "info: this is where '%v' was first declared",
-                                vorig->where_decl, NULL, 0, NULL);
-               }
-               do {
-                       ok = 1;
-                       propagate_types($5, c, &ok, $3, 0);
-               } while (ok == 2);
-               if (!ok)
-                       c->parse_error = 1;
-               else if (v) {
-                       struct value res = interp_exec(c, $5, &v->type);
-                       global_alloc(c, v->type, v, &res);
+                       v = var_ref(c, $1.txt);
+                       if (v->type == Tnone) {
+                               v->where_decl = var;
+                               v->where_set = var;
+                               v->type = $<CT;
+                               v->constant = 1;
+                               v->global = 1;
+                       } else {
+                               tok_err(c, "error: name already declared", &$1);
+                               type_err(c, "info: this is where '%v' was first declared",
+                                        v->where_decl, NULL, 0, NULL);
+                       }
                }
+               var->var = v;
+
+               bv = new(binode);
+               bv->op = Declare;
+               bv->left = var;
+               bv->right= $<Exp;
+
+               bl = new(binode);
+               bl->op = List;
+               bl->left = c->constlist;
+               bl->right = bv;
+               c->constlist = bl;
        } }$
 
-###### print const decls
+###### core functions
+       static void resolve_consts(struct parse_context *c)
        {
-               struct variable *v;
-               int target = -1;
-
-               while (target != 0) {
-                       int i = 0;
-                       for (v = context.in_scope; v; v=v->in_scope)
-                               if (v->depth == 0 && v->constant) {
-                                       i += 1;
-                                       if (i == target)
-                                               break;
+               struct binode *b;
+               int retry = 1;
+               enum { none, some, cannot } progress = none;
+
+               c->constlist = reorder_bilist(c->constlist);
+               while (retry) {
+                       retry = 0;
+                       for (b = cast(binode, c->constlist); b;
+                            b = cast(binode, b->right)) {
+                               enum prop_err perr;
+                               struct binode *vb = cast(binode, b->left);
+                               struct var *v = cast(var, vb->left);
+                               if (v->var->frame_pos >= 0)
+                                       continue;
+                               do {
+                                       perr = 0;
+                                       propagate_types(vb->right, c, &perr,
+                                                       v->var->type, 0);
+                               } while (perr & Eretry);
+                               if (perr & Efail)
+                                       c->parse_error += 1;
+                               else if (!(perr & Enoconst)) {
+                                       progress = some;
+                                       struct value res = interp_exec(
+                                               c, vb->right, &v->var->type);
+                                       global_alloc(c, v->var->type, v->var, &res);
+                               } else {
+                                       if (progress == cannot)
+                                               type_err(c, "error: const %v cannot be resolved.",
+                                                        v, NULL, 0, NULL);
+                                       else
+                                               retry = 1;
                                }
-
-                       if (target == -1) {
-                               if (i)
-                                       printf("const\n");
-                               target = i;
-                       } else {
-                               struct value *val = var_value(&context, v);
-                               printf("    %.*s :: ", v->name->name.len, v->name->name.txt);
-                               type_print(v->type, stdout);
-                               printf(" = ");
-                               if (v->type == Tstr)
-                                       printf("\"");
-                               print_value(v->type, val, stdout);
-                               if (v->type == Tstr)
-                                       printf("\"");
-                               printf("\n");
-                               target -= 1;
+                       }
+                       switch (progress) {
+                       case cannot:
+                               retry = 0; break;
+                       case none:
+                               progress = cannot; break;
+                       case some:
+                               progress = none; break;
                        }
                }
        }
 
+###### print const decls
+       {
+               struct binode *b;
+               int first = 1;
+
+               for (b = cast(binode, context.constlist); b;
+                    b = cast(binode, b->right)) {
+                       struct binode *vb = cast(binode, b->left);
+                       struct var *vr = cast(var, vb->left);
+                       struct variable *v = vr->var;
+
+                       if (first)
+                               printf("const\n");
+                       first = 0;
+
+                       printf("    %.*s :: ", v->name->name.len, v->name->name.txt);
+                       type_print(v->type, stdout);
+                       printf(" = ");
+                       print_exec(vb->right, -1, 0);
+                       printf("\n");
+               }
+       }
+
+###### free const decls
+       free_binode(context.constlist);
+
 ### Function declarations
 
 The code in an Ocean program is all stored in function declarations.
@@ -4956,7 +5103,7 @@ is a bit more interesting at this level.
                 * is a list for 'struct var'
                 */
                struct type *t = add_anon_type(c, &structure_prototype,
-                                              " function result");
+                                              "function result");
                int cnt = 0;
                struct binode *b;
 
@@ -5024,32 +5171,32 @@ is a bit more interesting at this level.
 
        $*variable
        DeclareFunction -> func FuncName ( OpenScope ArgsLine ) Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, Tnone, NULL, $<Bl);
-               }$
-               | func FuncName IN OpenScope Args OUT OptNL do Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, Tnone, NULL, $<Bl);
-               }$
-               | func FuncName NEWLINE OpenScope OptNL do Block Newlines ${
-                       $0 = declare_function(c, $<FN, NULL, Tnone, NULL, $<Bl);
-               }$
-               | func FuncName ( OpenScope ArgsLine ) : Type Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, $<Ty, NULL, $<Bl);
-               }$
-               | func FuncName ( OpenScope ArgsLine ) : ( ArgsLine ) Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<AL, NULL, $<AL2, $<Bl);
-               }$
-               | func FuncName IN OpenScope Args OUT OptNL return Type Newlines do Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, $<Ty, NULL, $<Bl);
-               }$
-               | func FuncName NEWLINE OpenScope return Type Newlines do Block Newlines ${
-                       $0 = declare_function(c, $<FN, NULL, $<Ty, NULL, $<Bl);
-               }$
-               | func FuncName IN OpenScope Args OUT OptNL return IN Args OUT OptNL do Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, NULL, $<Ar2, $<Bl);
-               }$
-               | func FuncName NEWLINE OpenScope return IN Args OUT OptNL do Block Newlines ${
-                       $0 = declare_function(c, $<FN, NULL, NULL, $<Ar, $<Bl);
-               }$
+               $0 = declare_function(c, $<FN, $<Ar, Tnone, NULL, $<Bl);
+       }$
+       | func FuncName IN OpenScope Args OUT OptNL do Block Newlines ${
+               $0 = declare_function(c, $<FN, $<Ar, Tnone, NULL, $<Bl);
+       }$
+       | func FuncName NEWLINE OpenScope OptNL do Block Newlines ${
+               $0 = declare_function(c, $<FN, NULL, Tnone, NULL, $<Bl);
+       }$
+       | func FuncName ( OpenScope ArgsLine ) : Type Block Newlines ${
+               $0 = declare_function(c, $<FN, $<Ar, $<Ty, NULL, $<Bl);
+       }$
+       | func FuncName ( OpenScope ArgsLine ) : ( ArgsLine ) Block Newlines ${
+               $0 = declare_function(c, $<FN, $<AL, NULL, $<AL2, $<Bl);
+       }$
+       | func FuncName IN OpenScope Args OUT OptNL return Type Newlines do Block Newlines ${
+               $0 = declare_function(c, $<FN, $<Ar, $<Ty, NULL, $<Bl);
+       }$
+       | func FuncName NEWLINE OpenScope return Type Newlines do Block Newlines ${
+               $0 = declare_function(c, $<FN, NULL, $<Ty, NULL, $<Bl);
+       }$
+       | func FuncName IN OpenScope Args OUT OptNL return IN Args OUT OptNL do Block Newlines ${
+               $0 = declare_function(c, $<FN, $<Ar, NULL, $<Ar2, $<Bl);
+       }$
+       | func FuncName NEWLINE OpenScope return IN Args OUT OptNL do Block Newlines ${
+               $0 = declare_function(c, $<FN, NULL, NULL, $<Ar, $<Bl);
+       }$
 
 ###### print func decls
        {
@@ -5090,20 +5237,20 @@ is a bit more interesting at this level.
                for (v = c->in_scope; v; v = v->in_scope) {
                        struct value *val;
                        struct type *ret;
-                       int ok = 1;
+                       enum prop_err perr;
                        if (v->depth != 0 || !v->type || !v->type->check_args)
                                continue;
                        ret = v->type->function.inline_result ?
                                Tnone : v->type->function.return_type;
                        val = var_value(c, v);
                        do {
-                               ok = 1;
-                               propagate_types(val->function, c, &ok, ret, 0);
-                       } while (ok == 2);
-                       if (ok)
+                               perr = 0;
+                               propagate_types(val->function, c, &perr, ret, 0);
+                       } while (!(perr & Efail) && (perr & Eretry));
+                       if (!(perr & Efail))
                                /* Make sure everything is still consistent */
-                               propagate_types(val->function, c, &ok, ret, 0);
-                       if (!ok)
+                               propagate_types(val->function, c, &perr, ret, 0);
+                       if (perr & Efail)
                                all_ok = 0;
                        if (!v->type->function.inline_result &&
                            !v->type->function.return_type->dup) {
@@ -5120,7 +5267,7 @@ is a bit more interesting at this level.
        {
                struct binode *bp = type->function.params;
                struct binode *b;
-               int ok = 1;
+               enum prop_err perr;
                int arg = 0;
                struct type *argv_type;
 
@@ -5129,16 +5276,16 @@ is a bit more interesting at this level.
                argv_type->array.unspec = 1;
 
                for (b = bp; b; b = cast(binode, b->right)) {
-                       ok = 1;
+                       perr = 0;
                        switch (arg++) {
                        case 0: /* argv */
-                               propagate_types(b->left, c, &ok, argv_type, 0);
+                               propagate_types(b->left, c, &perr, argv_type, 0);
                                break;
                        default: /* invalid */  // NOTEST
-                               propagate_types(b->left, c, &ok, Tnone, 0);     // NOTEST
+                               propagate_types(b->left, c, &perr, Tnone, 0);   // NOTEST
                        }
-                       if (!ok)
-                               c->parse_error = 1;
+                       if (perr & Efail)
+                               c->parse_error += 1;
                }
 
                return !c->parse_error;
@@ -5159,12 +5306,12 @@ is a bit more interesting at this level.
                        progp = var_value(c, mainv);
                if (!progp || !progp->function) {
                        fprintf(stderr, "oceani: no main function found.\n");
-                       c->parse_error = 1;
+                       c->parse_error += 1;
                        return;
                }
                if (!analyse_main(mainv->type, c)) {
                        fprintf(stderr, "oceani: main has wrong type.\n");
-                       c->parse_error = 1;
+                       c->parse_error += 1;
                        return;
                }
                al = mainv->type->function.params;