any expression cannot be evaluated a compile time, `Eruntime` is set.
If the expression can be copied, `Emaycopy` is set.
-If it remains unchanged at `0`, then no more propagation is needed.
+If `Erval` is set, then the value cannot be assigned to because it is
+a temporary result. If `Erval` is clear but `Econst` is set, then
+the value can only be assigned once, when the variable is declared.
###### ast
- enum val_rules {Rboolok = 1<<1, Rnoconstant = 1<<2};
+ enum val_rules {Rboolok = 1<<0,};
enum prop_err {Efail = 1<<0, Eretry = 1<<1, Eruntime = 1<<2,
- Emaycopy = 1<<3};
+ Emaycopy = 1<<3, Erval = 1<<4, Econst = 1<<5};
###### forward decls
static struct type *propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
###### core functions
static struct type *__propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
+ enum prop_err *perr_local,
struct type *type, int rules)
{
struct type *t;
struct type *type, int rules)
{
int pre_err = c->parse_error;
- struct type *ret = __propagate_types(prog, c, perr, type, rules);
+ enum prop_err perr_local = 0;
+ struct type *ret = __propagate_types(prog, c, perr, &perr_local, type, rules);
+ *perr |= perr_local & (Efail | Eretry);
if (c->parse_error > pre_err)
*perr |= Efail;
return ret;
if (!type_compat(type, val->vtype, rules))
type_err(c, "error: expected %1 found %2",
prog, type, rules, val->vtype);
+ *perr |= Erval;
return val->vtype;
}
if (!type_compat(type, Tlabel, rules))
type_err(c, "error: expected %1 found %2",
prog, type, rules, Tlabel);
+ *perr |= Erval;
return Tlabel;
}
###### interp exec cases
return Tnone; // NOTEST
}
v = v->merged;
- if (v->constant && (rules & Rnoconstant)) {
- type_err(c, "error: Cannot assign to a constant: %v",
- prog, NULL, 0, NULL);
- type_err(c, "info: name was defined as a constant here",
- v->where_decl, NULL, 0, NULL);
- return v->type;
- }
if (v->type == Tnone && v->where_decl == prog)
type_err(c, "error: variable used but not declared: %v",
prog, NULL, 0, NULL);
}
if (!v->global || v->frame_pos < 0)
*perr |= Eruntime;
+ if (v->constant)
+ *perr |= Econst;
if (!type)
return v->type;
return type;
/* left must be an array, right must be a number,
* result is the member type of the array
*/
- propagate_types(b->right, c, perr, Tnum, 0);
- t = propagate_types(b->left, c, perr, NULL, rules & Rnoconstant);
+ propagate_types(b->right, c, perr_local, Tnum, 0);
+ t = propagate_types(b->left, c, perr, NULL, 0);
if (!t || t->compat != array_compat) {
type_err(c, "error: %1 cannot be indexed", prog, t, 0, NULL);
return NULL;
r->reftype = type;
*perr |= Eretry;
}
+ *perr |= Erval;
return type;
case RefNil:
if (type && type->free != reference_free)
r->reftype = type;
*perr |= Eretry;
}
+ *perr |= Erval;
return type;
case RefFree:
- t = propagate_types(r->right, c, perr, NULL, 0);
+ t = propagate_types(r->right, c, perr_local, NULL, 0);
if (t && t->free != reference_free)
type_err(c, "error: @free can only be assigned a reference, not %1",
prog, t, 0, NULL);
/* left must be a reference, and we return what it refers to */
/* FIXME how can I pass the expected type down? */
t = propagate_types(b->left, c, perr, NULL, 0);
+ *perr &= ~Erval;
if (!t || t->free != reference_free)
type_err(c, "error: Cannot dereference %1", b, t, 0, NULL);
else
return NULL;
}
*perr |= Eruntime;
- v->var->type->check_args(c, perr, v->var->type, args);
+ v->var->type->check_args(c, perr_local, v->var->type, args);
if (v->var->type->function.inline_result)
*perr |= Emaycopy;
+ *perr |= Erval;
return v->var->type->function.return_type;
}
struct binode *b2 = cast(binode, b->right);
struct type *t2;
- propagate_types(b->left, c, perr, Tbool, 0);
+ propagate_types(b->left, c, perr_local, Tbool, 0);
t = propagate_types(b2->left, c, perr, type, 0);
t2 = propagate_types(b2->right, c, perr, type ?: t, 0);
return t ?: t2;
if (type && type != Tbool)
type_err(c, "error: %1 operation found where %2 expected", prog,
Tbool, 0, type);
+ *perr |= Erval;
return Tbool;
###### interp binode cases
if (!type_compat(type, Tbool, 0))
type_err(c, "error: Comparison returns %1 but %2 expected", prog,
Tbool, rules, type);
+ *perr |= Erval;
return Tbool;
###### interp binode cases
if (!type_compat(type, Tnum, 0))
type_err(c, "error: Arithmetic returns %1 but %2 expected", prog,
Tnum, rules, type);
+ *perr |= Erval;
return Tnum;
case Concat:
if (!type_compat(type, Tstr, 0))
type_err(c, "error: Concat returns %1 but %2 expected", prog,
Tstr, rules, type);
+ *perr |= Erval;
return Tstr;
case StringConv:
type_err(c, // UNTESTED
"error: Can only convert string to number, not %1",
prog, type, 0, NULL);
+ *perr |= Erval;
return Tnum;
case Test:
if (!t || !t->test)
type_err(c, "error: '?' requires a testable value, not %1",
prog, t, 0, NULL);
+ *perr |= Erval;
return Tbool;
case Choose:
if (t && t->test == NULL)
type_err(c, "error: \"??\" requires a testable value, not %1",
prog, t, 0, NULL);
+ *perr |= Erval;
return t;
case Bracket:
- return propagate_types(b->right, c, perr, type, 0);
+ return propagate_types(b->right, c, perr, type, rules);
###### interp binode cases
else
b = cast(binode, b->right);
while (b) {
- propagate_types(b->left, c, perr, NULL, 0);
+ propagate_types(b->left, c, perr_local, NULL, 0);
b = cast(binode, b->right);
}
break;
* For Assign, left must not be constant.
* result is Tnone
*/
- t = propagate_types(b->left, c, perr, NULL,
- (b->op == Assign ? Rnoconstant : 0));
+ *perr &= ~(Erval | Econst);
+ t = propagate_types(b->left, c, perr, NULL, 0);
if (!b->right)
return Tnone;
if (t) {
- if (propagate_types(b->right, c, perr, t, 0) != t)
+ if (propagate_types(b->right, c, perr_local, 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, perr, NULL, 0);
+ t = propagate_types(b->right, c, perr_local, NULL, 0);
if (t)
- propagate_types(b->left, c, perr, t,
- (b->op == Assign ? Rnoconstant : 0));
+ propagate_types(b->left, c, perr, t, 0);
+ }
+ if (*perr & Erval)
+ type_err(c, "error: cannot assign to an rval", b,
+ NULL, 0, NULL);
+ else if (b->op == Assign && (*perr & Econst)) {
+ type_err(c, "error: Cannot assign to a constant: %v",
+ b->left, NULL, 0, NULL);
+ if (b->left->type == Xvar) {
+ struct var *var = cast(var, b->left);
+ struct variable *v = var->var;
+ type_err(c, "info: name was defined as a constant here",
+ v->where_decl, NULL, 0, NULL);
+ }
}
- if (t && t->dup == NULL && !(*perr & Emaycopy))
+ if (t && t->dup == NULL && !(*perr_local & Emaycopy))
type_err(c, "error: cannot assign value of type %1", b, t, 0, NULL);
return Tnone;
###### propagate binode cases
case Loop:
- t = propagate_types(b->right, c, perr, Tnone, 0);
+ t = propagate_types(b->right, c, perr_local, Tnone, 0);
if (!type_compat(Tnone, t, 0))
*perr |= Efail; // UNTESTED
return propagate_types(b->left, c, perr, type, rules);