aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/SPEC.md4
-rw-r--r--src/compiler/builtins.zc8
-rw-r--r--src/compiler/expr.zc4
-rw-r--r--tests/conformance/2-types/enum_names_own_member.zc8
-rw-r--r--tests/conformance/known.txt1
5 files changed, 18 insertions, 7 deletions
diff --git a/docs/SPEC.md b/docs/SPEC.md
index f53c469..5f84acc 100644
--- a/docs/SPEC.md
+++ b/docs/SPEC.md
@@ -227,7 +227,7 @@ a struct cannot contain itself by value (error: a struct can't contain itself by
### 2.8 enums
-2.8.1 plain enum. `enum color { RED, GREEN = 5, BLUE };` declares the global constants `RED` 0, `GREEN` 5, `BLUE` 6, also reachable as `color.RED` in expressions and case labels. a member without a value is the previous value plus one, starting at 0. the name is optional (`enum { A, B };`). values are constant expressions (3.14). a member's value cannot name an earlier member of the same enum (the members are defined when the closing brace is reached; error: expected a constant). under `typecheck on;` the enum is a distinct type (2.9).
+2.8.1 plain enum. `enum color { RED, GREEN = 5, BLUE };` declares the global constants `RED` 0, `GREEN` 5, `BLUE` 6, also reachable as `color.RED` in expressions and case labels. a member without a value is the previous value plus one, starting at 0. the name is optional (`enum { A, B };`). values are constant expressions (3.14). a member is defined as soon as it is declared, so a later member's value can name it: `enum { HI = 16, LO = HI + 1 };` gives `LO` 17. under `typecheck on;` the enum is a distinct type (2.9).
2.8.2 tagged enum. when any member has a `{ fields }` block the enum is tagged: `enum shape { circle { int r; }, rect { int w; int h; }, dot };`. rules:
- it needs a name; payload fields are `int`, `float` or `let` (a word), at most 8 per member; at most 63 members; members take no explicit values.
@@ -1105,7 +1105,7 @@ each item gives a minimal program, the expected behavior (per this spec) and the
### b.2 implementation bugs and limitations
1. fixed: `memdebug scrub;` is accepted and poisons freed payloads with 57005 (b.1.1).
-2. enum members cannot name earlier members of the same enum: `enum { HI = 16, LO = HI + 1 };` => `expected a constant` (the members are defined after the closing brace). members of an earlier enum and constants work.
+2. fixed (2.8.1): enum members could not name earlier members of the same enum: `enum { HI = 16, LO = HI + 1 };` => `expected a constant` (the members are defined after the closing brace). members of an earlier enum and constants work.
3. fixed (now error: f: is defined twice): a function could be silently redefined. `int f() { return 1; } int g() { return f() + 10; } int f() { return 2; }`, then `g()` gives 11 and a later call of `f()` gives 2. expected: error `f: is defined twice` (the message exists in the compiler and is unreachable for an ordinary second definition).
4. fixed (the definition stays): a prototype after the definition hid it. `int f() { return 1; } int f();` then `f()` => `zc: line 0: f is used but never defined`. expected: the definition stays.
5. fixed (3.5.5): `?:` with one float branch and one integer branch converted wrongly. `pf(1 ? 1 : 2.5)` prints `0.000000` (the integer 1's bits read as a float); `pf(1 ? 2.5 : 1)` prints `4612811918334230528.000000` (the taken float converted again as if an integer); `pf(0 ? 2.5 : 1)` prints 1.000000 and `pf(0 ? 1 : 2.5)` 2.500000 (these two are right). expected 1.0, 2.5, 1.0, 2.5.
diff --git a/src/compiler/builtins.zc b/src/compiler/builtins.zc
index 214d493..6dc586a 100644
--- a/src/compiler/builtins.zc
+++ b/src/compiler/builtins.zc
@@ -71,8 +71,10 @@ int ptagged(word t) {
// enum name { A, B = 5 } is a plain enum: each member is a constant, plain (A)
// and scoped (name.A). a member with a block of fields makes it a tagged enum
// (see enum_tagged): enum name { circle { int r; }, dot }.
+// the first member of the enum being read, so a member's value can name the members before it; -1 outside
+int enum_open = 0 - 1;
int kw_enum() {
- char* n; char* en = 0; int first = npm, cnt = 0, tagged = 0, gi, nf, v = 0, et = 0, sk, ft; char* fname;
+ char* n; char* en = 0; int first = npm, cnt = 0, tagged = 0, gi, nf, v = 0, et = 0, sk, ft, rv = 0; char* fname;
stmtonly();
next();
if (tk == T_ID) { en = tv; next(); }
@@ -97,7 +99,9 @@ int kw_enum() {
expect(s_semi);
}
expect(s_rb);
- } else if (accept(s_eq)) { pm_val[gi] = cexpr(); pm_exp[gi] = 1; }
+ } else { enum_open = first; if (accept(s_eq)) { pm_val[gi] = cexpr(); pm_exp[gi] = 1; } enum_open = 0 - 1; }
+ if (pm_exp[gi]) rv = pm_val[gi]; else if (!pm_nf[gi]) pm_val[gi] = rv; // the running value, read by cprim
+ rv++;
cnt++;
if (!accept(s_comma)) break;
if (is_op(s_rb)) break;
diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc
index 230a2d3..e449508 100644
--- a/src/compiler/expr.zc
+++ b/src/compiler/expr.zc
@@ -1959,6 +1959,10 @@ word cprim() {
}
}
}
+ if (tk == T_ID && enum_open >= 0) { // an earlier member of the enum being read (spec 2.8.1)
+ mc = npm - 1;
+ while (mc > enum_open) { mc--; if (pm_name[mc] == tv && !pm_nf[mc]) { next(); return pm_val[mc]; } }
+ }
die("expected a constant");
return 0;
}
diff --git a/tests/conformance/2-types/enum_names_own_member.zc b/tests/conformance/2-types/enum_names_own_member.zc
index 26d4739..1a49106 100644
--- a/tests/conformance/2-types/enum_names_own_member.zc
+++ b/tests/conformance/2-types/enum_names_own_member.zc
@@ -1,4 +1,8 @@
-// expect error: expected a constant
+// expect: 17/3/6/
+// spec 2.8.1: a member is defined as soon as it is declared; a later value can name it
include rt;
+void pn(int x) { rt.put_n(x); rt.put_c(10); }
enum e { HI = 16, LO = HI + 1 };
-fn main() { return 0; }
+enum f { A, B, C, D = C + 1, E = D * 2 };
+fn main() { pn(LO); pn(D); pn(E); return 0; }
+// zc0: skip
diff --git a/tests/conformance/known.txt b/tests/conformance/known.txt
index 22a1e3d..723098a 100644
--- a/tests/conformance/known.txt
+++ b/tests/conformance/known.txt
@@ -2,7 +2,6 @@
# class a: self-hosted compiler bug; b: zc0 deviation (appendix a/b); c: spec issue to settle
# appendix b.2.8 (a global named in an initializer is its address) is documented behavior per 5.3.2 and 3.14.2: no entry
4-statements/o3_outside_function_ordinary_block.zc zy c top-level o3 { } is rejected; spec 4.10.2 says it is an ordinary block
-5-declarations/enum_member_names_earlier_b2.zc zy c appendix b.2.2 confirmed, but spec 2.8.1 documents it (members are defined at the closing brace); not fixed, like b.2.8 the spec body wins until decided
5-declarations/fn_prototype_unnamed_params_b13.zc zy c spec 5.2.1 says prototype parameter names are required, and a type name alone is read as an untyped parameter of that name (paramstart); b.2.13 is a spec choice, not fixed
9-appendix-b/b2_06_bool_normalises.zc zy c appendix b.2.6 confirmed, but spec 2.1 documents bool as a number that is not normalised
9-appendix-b/b2_10_recovery_hides_errors.zc zy c appendix b.2.10 confirmed; by design per 8.2