aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/SPEC.md4
-rw-r--r--src/compiler/decl.zc13
-rw-r--r--tests/conformance/5-declarations/fn_prototype_unnamed_pointer_params.zc8
-rw-r--r--tests/conformance/known.txt1
-rw-r--r--tests/langerrors/param_unnamed_with_body.zc3
5 files changed, 24 insertions, 5 deletions
diff --git a/docs/SPEC.md b/docs/SPEC.md
index 30d547d..128f961 100644
--- a/docs/SPEC.md
+++ b/docs/SPEC.md
@@ -683,7 +683,7 @@ node*.u make() { ... } // owned result (.u)
fn f(a, b) { ... } // untyped; types are inferred
fn f(int a, b) { ... } // parameters may mix
T first<T>(T* p) { ... } // generic
-int proto(int n); // prototype (parameter names are required)
+int proto(int n); // prototype (int proto(int); also works: a prototype's parameter names are optional)
```
a bare `name(params) { ... }` with no `fn` and no type is not a function declaration (error: expected a declaration).
@@ -1116,7 +1116,7 @@ each item gives a minimal program, the expected behavior (per this spec) and the
10. a syntax error hides errors in the statements skipped by recovery: `int a = ; int b = 3 +;` reports only the first error (8.2). by design, but easy to miss.
11. fixed (1.2.2): an unterminated `/* ...` comment was accepted silently; it is error: unterminated comment. (an unterminated string literal is still only reported as a later syntax error.)
12. settled (3.2.3): `x as int * y` parses `int*` as the type. this is the rule (the type is read greedily, as zc0 does too), so the program is a syntax error.
-13. prototypes need parameter names: `int f(int);` => `expected a name, found ')'`.
+13. fixed (5.2.1): a prototype's parameters may be unnamed (`int f(int, int*);`). a lone type name counts as a type only in a prototype; with a body it is still the parameter's name, and an unnamed typed parameter there is `a parameter needs a name`.
14. `len(s)` on a `char*` returns word 0 of the string when `typecheck` is off.
15. duplicate `case` labels are not diagnosed (4.7.2).
16. a local `const` cannot be used in a constant expression (b.1.7).
diff --git a/src/compiler/decl.zc b/src/compiler/decl.zc
index e9500c4..87281ad 100644
--- a/src/compiler/decl.zc
+++ b/src/compiler/decl.zc
@@ -282,7 +282,7 @@ int body() {
// into stype, checked against any earlier prototype or prescan result, and
// where the o3 region for an o3 function is handed to the core)
int fndef(int chk, int rty, int ret, int n) {
- int s, np = 0, tags = 0, t, sig, i; char*[24] pn; word[24] pt; word[24] pty; int typed = 0, ft, explicit_rty = !!rty, o3f = o3nxt, lo, hi, pk, lt, lend, lfl = tline, redef;
+ int s, np = 0, tags = 0, t, sig, i; char*[24] pn; word[24] pt; word[24] pty; int typed = 0, ft, explicit_rty = !!rty, o3f = o3nxt, lo, hi, pk, lt, lend, lfl = tline, redef, amb = 0, nonm = 0;
o3nxt = 0;
if (rty || ntp) typed = 1;
if (!dnm) dnm = n;
@@ -327,7 +327,9 @@ int fndef(int chk, int rty, int ret, int n) {
} else if (tv == s_d) t = 2; else die("a parameter tag is .m or .d");
next();
}
- pnoffs[np] = toff; pnplaces[np] = tpack(); rs_note(RS_PARAM, toff); pn[np] = name();
+ pnoffs[np] = toff; pnplaces[np] = tpack(); rs_note(RS_PARAM, toff);
+ if (!pk && !t && tk == T_ID && istypename(tv)) amb = amb | (1 << np); // int f(int): a name, or a type if this is a prototype
+ if (pk && tk == T_OP && (tv == s_comma || tv == s_rp)) { pn[np] = 0; nonm = 1; } else pn[np] = name(); // int f(int*, int): a prototype's parameters may be unnamed
if (o3f && pty[np] == tword) serr("o3 parameters need a type");
if (np >= 12 && t) die("only the first 12 parameters can be tagged");
pt[np] = t;
@@ -338,6 +340,13 @@ int fndef(int chk, int rty, int ret, int n) {
}
}
expect(s_rp);
+ if (is_op(s_semi)) { // a prototype: a lone type name is an unnamed parameter of that type (b.2.13)
+ i = 0;
+ while (i < np) {
+ if (((amb >> i) & 1) && pn[i]) { t = lookup(pn[i]); if (t >= 0 && skind[t] == K_TYPE) { pty[i] = sval[t]; typed = 1; } }
+ i++;
+ }
+ } else if (nonm) serr("a parameter needs a name");
sig = ret + np + np + tags * 64;
if ((sflag[s] & F_SIG) && !(sflag[s] & F_PRE)) {
if (ssig[s] != sig) serrn(n, "doesn't match its earlier declaration");
diff --git a/tests/conformance/5-declarations/fn_prototype_unnamed_pointer_params.zc b/tests/conformance/5-declarations/fn_prototype_unnamed_pointer_params.zc
new file mode 100644
index 0000000..7136bee
--- /dev/null
+++ b/tests/conformance/5-declarations/fn_prototype_unnamed_pointer_params.zc
@@ -0,0 +1,8 @@
+// expect: 7/
+// spec 5.2.1: a prototype's parameters may be unnamed, pointers included
+include rt;
+void pn(int x) { rt.put_n(x); rt.put_c(10); }
+int f(int, int*);
+fn main() { int[1] a; a[0] = 5; pn(f(2, a)); return 0; }
+int f(int k, int* p) { return k + p[0]; }
+// zc0: skip
diff --git a/tests/conformance/known.txt b/tests/conformance/known.txt
index 7cb703e..2fbd331 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/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_10_recovery_hides_errors.zc zy c appendix b.2.10 confirmed; by design per 8.2
9-appendix-b/b2_14_len_char_ptr_typecheck_off.zc zy c appendix b.2.14 confirmed, but spec 3.13.5 leaves it unspecified
9-appendix-b/b2_15_duplicate_case.zc zy c appendix b.2.15 confirmed; 4.7.2 says duplicates are allowed
diff --git a/tests/langerrors/param_unnamed_with_body.zc b/tests/langerrors/param_unnamed_with_body.zc
new file mode 100644
index 0000000..bf96698
--- /dev/null
+++ b/tests/langerrors/param_unnamed_with_body.zc
@@ -0,0 +1,3 @@
+// expect: a parameter needs a name
+int f(int*) { return 1; }
+fn main() { return 0; }