From 42abc0d7e77812a62974a553fbc0d4cfaca2df26 Mon Sep 17 00:00:00 2001 From: Sebastian Jeong Date: Sun, 16 Aug 2026 18:23:30 +0900 Subject: [PATCH] feat: align M3 slices and strings with v0.1.7 --- fec/src/ast.c | 2 +- fec/src/ast.h | 1 + fec/src/check.c | 53 ++++++++++----- fec/src/emit_c.c | 68 +++++++++++++------ fec/src/parser.c | 1 + fec/src/types.c | 32 +++++++-- fec/src/types.h | 1 + fec/test-dos.bat | 56 +++++++++------ .../fail/{logical-symbols.fe => logical.fe} | 0 .../fail/{missing-semi.fe => misssemi.fe} | 0 .../fail/{unclosed-comment.fe => unclcomm.fe} | 0 fec/tests/m2/{bad-arity.fe => bad-ari.fe} | 0 fec/tests/m2/{bad-assign.fe => bad-asgn.fe} | 0 .../m2/{bad-condition.fe => bad-cond.fe} | 0 fec/tests/m2/{bad-return.fe => bad-ret.fe} | 0 fec/tests/m2/{bad-types.fe => bad-type.fe} | 0 fec/tests/m2/{bad-uninit.fe => bad-unit.fe} | 0 fec/tests/m2/{bad-unknown.fe => bad-unk.fe} | 0 fec/tests/m2/{cast-while.fe => castwhil.fe} | 0 fec/tests/m3/bad-mlet.fe | 6 ++ fec/tests/m3/bad-shwr.fe | 5 ++ fec/tests/m3/mutable.fe | 11 +++ fec/tests/m3/slcbound.fe | 7 ++ fec/tests/m4/{bad-arity.fe => bad-ari.fe} | 0 fec/tests/m4/{bad-runtime.fe => bad-run.fe} | 0 fec/tests/m4/{bad-writer.fe => bad-writ.fe} | 0 fec/tests/m4/{try-fprint.fe => try-fpr.fe} | 0 .../m5/{bad-conditional.fe => bad-cond.fe} | 0 fec/tests/m5/{bad-double.fe => bad-dbl.fe} | 0 fec/tests/m5/{bad-destroy.fe => bad-dest.fe} | 0 .../{keywords-and-builtins.fe => keybuilt.fe} | 0 fec/tests/pass/{v012-forms.fe => v012form.fe} | 0 32 files changed, 183 insertions(+), 60 deletions(-) rename fec/tests/fail/{logical-symbols.fe => logical.fe} (100%) rename fec/tests/fail/{missing-semi.fe => misssemi.fe} (100%) rename fec/tests/fail/{unclosed-comment.fe => unclcomm.fe} (100%) rename fec/tests/m2/{bad-arity.fe => bad-ari.fe} (100%) rename fec/tests/m2/{bad-assign.fe => bad-asgn.fe} (100%) rename fec/tests/m2/{bad-condition.fe => bad-cond.fe} (100%) rename fec/tests/m2/{bad-return.fe => bad-ret.fe} (100%) rename fec/tests/m2/{bad-types.fe => bad-type.fe} (100%) rename fec/tests/m2/{bad-uninit.fe => bad-unit.fe} (100%) rename fec/tests/m2/{bad-unknown.fe => bad-unk.fe} (100%) rename fec/tests/m2/{cast-while.fe => castwhil.fe} (100%) create mode 100644 fec/tests/m3/bad-mlet.fe create mode 100644 fec/tests/m3/bad-shwr.fe create mode 100644 fec/tests/m3/mutable.fe create mode 100644 fec/tests/m3/slcbound.fe rename fec/tests/m4/{bad-arity.fe => bad-ari.fe} (100%) rename fec/tests/m4/{bad-runtime.fe => bad-run.fe} (100%) rename fec/tests/m4/{bad-writer.fe => bad-writ.fe} (100%) rename fec/tests/m4/{try-fprint.fe => try-fpr.fe} (100%) rename fec/tests/m5/{bad-conditional.fe => bad-cond.fe} (100%) rename fec/tests/m5/{bad-double.fe => bad-dbl.fe} (100%) rename fec/tests/m5/{bad-destroy.fe => bad-dest.fe} (100%) rename fec/tests/pass/{keywords-and-builtins.fe => keybuilt.fe} (100%) rename fec/tests/pass/{v012-forms.fe => v012form.fe} (100%) diff --git a/fec/src/ast.c b/fec/src/ast.c index 63fdff7..561b834 100644 --- a/fec/src/ast.c +++ b/fec/src/ast.c @@ -8,7 +8,7 @@ FeNode *fe_node(FeAst *a, FeNodeKind k, FeLoc loc, const char *text, unsigned lo FeNode *n=(FeNode *)fe_arena_alloc(&a->arena,sizeof(FeNode)); if (!n) return 0; n->kind=k; n->loc=loc; n->text=text?fe_arena_strdup(&a->arena,text,len):0; - n->a=n->b=n->c=n->children=n->next=0; n->cname=0; n->aux_text=0; n->aux_cname=0; n->sem_type=0; n->flags=0; return n; + n->a=n->b=n->c=n->children=n->next=0; n->cname=0; n->aux_text=0; n->aux_cname=0; n->sem_type=0; n->sem_decl=0; n->flags=0; return n; } void fe_node_add(FeNode *parent, FeNode *child) { diff --git a/fec/src/ast.h b/fec/src/ast.h index 33140e4..02b3743 100644 --- a/fec/src/ast.h +++ b/fec/src/ast.h @@ -30,6 +30,7 @@ struct FeNode { char *aux_text; char *aux_cname; FeType *sem_type; + FeNode *sem_decl; unsigned flags; }; diff --git a/fec/src/check.c b/fec/src/check.c index 912936c..4bda576 100644 --- a/fec/src/check.c +++ b/fec/src/check.c @@ -54,7 +54,7 @@ static int is_copy_type(FeType *t) unsigned i; if (!t) return 1; if (t->kind==FE_TYPE_OWNED) return 0; - if (t->kind==FE_TYPE_REF) return !t->ref_mut; + if (t->kind==FE_TYPE_REF || t->kind==FE_TYPE_SLICE) return !t->ref_mut; if (t->kind==FE_TYPE_ARRAY) return is_copy_type(t->elem); if (t->kind==FE_TYPE_STRUCT) { if (t->has_drop) return 0; @@ -311,6 +311,8 @@ static FeNode *find_const_node(FeCheck *c, const char *name) return 0; } +static int format_is_slice_u8(FeType *t); + static const char *builtin_format(FeCheckerState *s, FeNode *fmt) { FeNode *decl; @@ -323,7 +325,7 @@ static const char *builtin_format(FeCheckerState *s, FeNode *fmt) sym->decl : find_const_node(s->c,fmt->text); if (decl && decl->b && decl->b->kind==FE_N_LITERAL && decl->b->text && decl->b->text[0]=='"') { - if (!decl->a || fe_type_from_ast(&s->c->types,decl->a)->kind==FE_TYPE_STR) + if (!decl->a || format_is_slice_u8(fe_type_from_ast(&s->c->types,decl->a))) return decl->b->text; } } @@ -347,10 +349,10 @@ static int format_arg_ok(FeType *t, int verb) if (!t) return 0; if (verb=='x') return fe_type_is_integer(t); if (verb=='c') return t->kind==FE_TYPE_CHAR; - if (verb=='s') return t->kind==FE_TYPE_STR || format_is_slice_u8(t); + if (verb=='s') return format_is_slice_u8(t); if (verb=='b') return t->kind==FE_TYPE_BOOL; if (t->kind==FE_TYPE_INT || t->kind==FE_TYPE_BOOL || - t->kind==FE_TYPE_CHAR || t->kind==FE_TYPE_STR) return 1; + t->kind==FE_TYPE_CHAR) return 1; return format_is_slice_u8(t) || (t->kind==FE_TYPE_ENUM && t->is_error); } @@ -381,7 +383,8 @@ static void check_format_call(FeCheckerState *s, FeNode *n) if (strcmp(n->text,"@sprint")==0) { if (!fmt_node) { err(s->c,n->loc,"@sprint requires a buffer"); return; } t=check_expr(s,fmt_node); - if (!format_is_slice_u8(t)) err(s->c,fmt_node->loc,"@sprint requires []u8 buffer"); + if (!format_is_slice_u8(t) || !t->ref_mut) + err(s->c,fmt_node->loc,"@sprint requires []mut u8 buffer"); fmt_node=fmt_node->next; } fmt=builtin_format(s,fmt_node); @@ -517,7 +520,18 @@ static FeType *check_index(FeCheckerState *s, FeNode *n) if(n->c || !n->b) { if (base->kind==FE_TYPE_ARRAY && !array_slice_lvalue(n->a)) err(s->c,n->loc,"array slicing requires a stable lvalue"); - if(n->c) { idx=check_expr(s,n->c); if(known(idx)&&!fe_type_is_integer(idx)) err(s->c,n->loc,"slice bound must be an integer"); } elem=base->elem; n->sem_type=fe_type_slice(&s->c->types,elem); if(base->kind==FE_TYPE_STR) n->flags|=2U; return n->sem_type; } + if(n->c) { + idx=check_expr(s,n->c); + if(known(idx)&&!fe_type_is_integer(idx)) + err(s->c,n->loc,"slice bound must be an integer"); + } + elem=base->elem; + n->sem_type=(base->kind==FE_TYPE_SLICE ? base->ref_mut : + lvalue_writable(s,n->a)) ? + fe_type_mut_slice(&s->c->types,elem) : + fe_type_slice(&s->c->types,elem); + return n->sem_type; + } n->sem_type=base->elem; return n->sem_type; } @@ -715,6 +729,7 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) return unknown(c); } n->a->cname = sym->cname; + n->sem_decl = sym->fn; if (!sym->fn) { err(c, n->loc, "name is not a function"); return unknown(c); @@ -725,7 +740,10 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) a = check_expr(s, arg); mark_moved(s,arg,a); b = node_type(c, param->a); - if (!compatible(b, a, arg) && a->kind != FE_TYPE_UNKNOWN) + if (!compatible(b, a, arg) && + !(b && a && b->kind==FE_TYPE_SLICE && a->kind==FE_TYPE_SLICE && + !b->ref_mut && a->ref_mut && fe_type_equal(b->elem,a->elem)) && + a->kind != FE_TYPE_UNKNOWN) err(c, arg->loc, "argument type mismatch"); param = param->next; arg = arg->next; @@ -819,14 +837,15 @@ static FeType *check_lvalue(FeCheckerState *s, FeNode *n, int read) n->sem_type=field->type; return field->type; } if (n && n->kind == FE_N_INDEX) { - if (n->a && n->a->sem_type && n->a->sem_type->kind == FE_TYPE_STR) { - err(s->c, n->loc, "str is immutable"); - } - if (n->a && n->a->kind == FE_N_INDEX && (n->a->flags & 2U)) - err(s->c, n->loc, "str slice is immutable"); - if (!lvalue_writable(s,n->a)) - err(s->c,n->loc,"cannot assign through immutable value"); base=check_index(s,n); + if (n->a && n->a->sem_type && + n->a->sem_type->kind == FE_TYPE_SLICE && + !n->a->sem_type->ref_mut) + err(s->c,n->loc,"cannot write through shared slice"); + else if (n->a && n->a->sem_type && + n->a->sem_type->kind != FE_TYPE_SLICE && + !lvalue_writable(s,n->a)) + err(s->c,n->loc,"cannot assign through immutable value"); return base; } if (n) err(s->c, n->loc, "assignment requires a variable"); @@ -903,8 +922,8 @@ static void check_for(FeCheckerState *s, FeNode *n) else if (n->a && n->a->kind==FE_N_INDEX && n->a->a && n->a->a->kind==FE_N_IDENT) iter_sym=find_symbol(s->scope,n->a->a->text ? n->a->a->text : ""); - iter_mut=iter_sym && iter_sym->mutable; - if (start->kind==FE_TYPE_STR) iter_mut=0; + iter_mut=start->kind==FE_TYPE_SLICE ? start->ref_mut : + (iter_sym && iter_sym->mutable); ref_type=fe_type_ref(&s->c->types,elem,iter_mut); if (iter_mut) n->flags |= 4U; s->scope=scope_new(s,old); @@ -1017,6 +1036,8 @@ static void check_stmt(FeCheckerState *s, FeNode *n) err(c, n->loc, "initializer type mismatch"); if (b->kind == FE_TYPE_VOID) err(c, n->loc, "void expression cannot initialize a variable"); + if (n->kind==FE_N_LET && a->kind==FE_TYPE_SLICE && a->ref_mut) + err(c,n->loc,"let cannot bind a mutable slice"); mark_moved(s,n->b,b); add_symbol(s, s->scope, n->text, a, 0, 0, 1, local_cname(c, n->text ? n->text : "local"), n); diff --git a/fec/src/emit_c.c b/fec/src/emit_c.c index 102a2c5..f445afc 100644 --- a/fec/src/emit_c.c +++ b/fec/src/emit_c.c @@ -75,8 +75,10 @@ static void emit_one_type(FeEmitter *e, FeType *t) } else if(t->kind==FE_TYPE_ARRAY) { fputs(t->cname,e->out); fputs(" { ",e->out); fputs(fe_type_c_name(t->elem,e->pointer_bits),e->out); fputs(" a[",e->out); fprintf(e->out,"%lu",t->length); fputs("]; };\n",e->out); } else if(t->kind==FE_TYPE_SLICE && t->cname) { - fputs("typedef struct { ",e->out); fputs(fe_type_c_name(t->elem,e->pointer_bits),e->out); fputs(" *p; unsigned long n; } ",e->out); fputs(t->cname,e->out); fputs(";\n",e->out); - fprintf(e->out,"static %s %s(%s *p, unsigned long n) { %s s; s.p=p; s.n=n; return s; }\n",t->cname,t->maker,fe_type_c_name(t->elem,e->pointer_bits),t->cname); + fputs("typedef struct { ",e->out); + if(!t->ref_mut) fputs("const ",e->out); + fputs(fe_type_c_name(t->elem,e->pointer_bits),e->out); fputs(" *p; unsigned long n; } ",e->out); fputs(t->cname,e->out); fputs(";\n",e->out); + fprintf(e->out,"static %s %s(%s%s *p, unsigned long n) { %s s; s.p=p; s.n=n; return s; }\n",t->cname,t->maker,t->ref_mut ? "" : "const ",fe_type_c_name(t->elem,e->pointer_bits),t->cname); } else if(t->kind==FE_TYPE_ERROR_UNION) { fputs(t->cname,e->out); fputs(" { unsigned short e; ",e->out); fputs(fe_type_c_name(t->error_value,e->pointer_bits),e->out); @@ -97,8 +99,6 @@ static void emit_one_type(FeEmitter *e, FeType *t) static void emit_type_defs(FeEmitter *e) { FeType *t; - fputs("typedef struct { const unsigned char *p; unsigned long n; } fe_str;\n",e->out); - fputs("static fe_str fe_make_str(const unsigned char *p, unsigned long n) { fe_str s; s.p=p; s.n=n; return s; }\n",e->out); /* Every fixed array has a slice conversion helper, even if this unit only indexes the array. Intern those result types before emission so their typedefs are present before helper definitions. */ @@ -246,14 +246,6 @@ static void emit_type_helpers(FeEmitter *e) fprintf(e->out,"static %s %s(%s x, unsigned long a) { return %s(x,a,x.n); }\n",t->cname,t->tail_slicer,t->cname,t->slicer); } } - fputs("static unsigned char fe_idx_str(fe_str x, unsigned long i) { ",e->out); - if(!e->no_checks) fputs("if (i >= x.n) fe_trap_bounds(); ",e->out); - fputs("return x.p[i]; }\n",e->out); - fputs("static fe_str fe_slice_str(fe_str x, unsigned long a, unsigned long b) { ",e->out); - if(!e->no_checks) fputs("if (a > b || b > x.n) fe_trap_bounds(); ",e->out); - fputs("return fe_make_str(x.p+a,b-a); }\n",e->out); - fputs("static fe_str fe_full_slice_str(fe_str x) { return fe_slice_str(x,0,x.n); }\n",e->out); - fputs("static fe_str fe_tail_slice_str(fe_str x, unsigned long a) { return fe_slice_str(x,a,x.n); }\n",e->out); } static void emit_m4_runtime(FeEmitter *e) @@ -276,7 +268,6 @@ static void emit_m4_runtime(FeEmitter *e) fputs("unsigned long fe_m4_sprint_finish(void) { unsigned long result; if (!fe_m4_sprint_depth) abort(); --fe_m4_sprint_depth; result=fe_m4_sprint_stack[fe_m4_sprint_depth].start_n-fe_m4_sprint_stack[fe_m4_sprint_depth].b.n; return result; }\n",e->out); fputs("unsigned short fe_m4_write_bytes(fe_writer w, const unsigned char *p, unsigned long n) { return w.write_fn ? w.write_fn(w.ctx,p,n) : 1; }\n",e->out); fputs("unsigned short fe_m4_write_cstr(fe_writer w, const char *p) { return fe_m4_write_bytes(w,(const unsigned char*)p,(unsigned long)strlen(p)); }\n",e->out); - fputs("unsigned short fe_m4_write_str(fe_writer w, fe_str s) { return fe_m4_write_bytes(w,s.p,s.n); }\n",e->out); fputs("#define fe_m4_write_slice(w,s) fe_m4_write_bytes((w),(s).p,(s).n)\n",e->out); fputs("unsigned short fe_m4_write_int(fe_writer w, long v) { char b[40]; sprintf(b,\"%ld\",v); return fe_m4_write_cstr(w,b); }\n",e->out); fputs("unsigned short fe_m4_write_hex(fe_writer w, unsigned long v) { char b[40]; sprintf(b,\"%lx\",v); return fe_m4_write_cstr(w,b); }\n",e->out); @@ -504,9 +495,8 @@ static void emit_m4_arg(FeEmitter *e, FeNode *arg, int verb, if (verb=='b') { fputs("fe_m4_write_bool(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", ",e->out); emit_expr(e,arg); fputc(')',e->out); return; } - if (verb=='s' || (verb==' ' && t && (t->kind==FE_TYPE_STR || t->kind==FE_TYPE_SLICE))) { - if (t && t->kind==FE_TYPE_STR) fputs("fe_m4_write_str(",e->out); - else fputs("fe_m4_write_slice(",e->out); + if (verb=='s' || (verb==' ' && t && t->kind==FE_TYPE_SLICE)) { + fputs("fe_m4_write_slice(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", ",e->out); emit_expr(e,arg); fputc(')',e->out); return; } if (error_value) { @@ -651,7 +641,29 @@ static FeNode *init_field(FeNode *n, const char *name) static void emit_slice_call(FeEmitter *e, FeNode *n) { FeType *bt=n->a ? n->a->sem_type : 0; - const char *maker=bt && bt->slicer ? bt->slicer : "fe_slice_str"; + const char *maker=n->sem_type && n->sem_type->maker ? n->sem_type->maker : + (bt && bt->slicer ? bt->slicer : "fe_missing_slice"); + if (bt && (bt->kind==FE_TYPE_ARRAY || bt->kind==FE_TYPE_SLICE)) { + fputs(n->sem_type && n->sem_type->slicer ? + n->sem_type->slicer : "fe_missing_slicer",e->out); + fputc('(',e->out); fputs(maker,e->out); fputc('(',e->out); + if (bt->kind==FE_TYPE_ARRAY) { + fputs("(&",e->out); emit_lvalue(e,n->a); fputs(")->a",e->out); + } else { + emit_expr(e,n->a); fputs(".p",e->out); + } + fputs(", ",e->out); + if(bt->kind==FE_TYPE_ARRAY) fprintf(e->out,"%lu",bt->length); + else { emit_expr(e,n->a); fputs(".n",e->out); } + fputs("), ",e->out); + if(n->b) emit_expr(e,n->b); else fputs("0",e->out); + fputs(", ",e->out); + if(n->c) emit_expr(e,n->c); + else if(bt->kind==FE_TYPE_ARRAY) fprintf(e->out,"%lu",bt->length); + else { emit_expr(e,n->a); fputs(".n",e->out); } + fputc(')',e->out); + return; + } if (!n->b && !n->c && bt && bt->full_slicer) { fputs(bt->full_slicer,e->out); if (bt->kind==FE_TYPE_ARRAY) { fputs("(&",e->out); emit_lvalue(e,n->a); } @@ -698,7 +710,13 @@ static void emit_expr(FeEmitter *e, FeNode *n) case FE_N_LITERAL: if (n->text && strcmp(n->text, "true") == 0) fputs("1", e->out); else if (n->text && strcmp(n->text, "false") == 0) fputs("0", e->out); - else if (n->text && n->text[0]=='"') { fputs("fe_make_str((const unsigned char*)",e->out); emit_c_literal(e->out,n->text,1); fputs(", sizeof(",e->out); emit_c_literal(e->out,n->text,1); fputs(")-1)",e->out); } + else if (n->text && n->text[0]=='"') { + fputs(n->sem_type && n->sem_type->maker ? + n->sem_type->maker : "fe_missing_str",e->out); + fputs("((const unsigned char*)",e->out); + emit_c_literal(e->out,n->text,1); fputs(", sizeof(",e->out); + emit_c_literal(e->out,n->text,1); fputs(")-1)",e->out); + } else if (n->text && n->text[0]=='\'') emit_c_literal(e->out,n->text,0); else fputs(n->text ? n->text : "0", e->out); break; @@ -775,6 +793,7 @@ static void emit_expr(FeEmitter *e, FeNode *n) break; case FE_N_CALL: { FeVariantType *v; + FeNode *call_param=0; int special=0; if(n->text && (strcmp(n->text,"@print")==0 || strcmp(n->text,"@fprint")==0 || strcmp(n->text,"@sprint")==0)) { emit_m4_builtin(e,n); special=1; } else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && @@ -814,14 +833,25 @@ static void emit_expr(FeEmitter *e, FeNode *n) } else if (n->a) emit_expr(e, n->a); else fputs(n->text ? n->text : "fe_builtin", e->out); if(!special) { + if(n->sem_decl && n->sem_decl->kind==FE_N_FN && n->sem_decl->a) + call_param=n->sem_decl->a->children; fputc('(', e->out); for (x = n->children; x; x = x->next) { + FeType *want=call_param && call_param->a ? + fe_type_from_ast(&e->check->types,call_param->a) : 0; if (x != n->children) fputs(", ", e->out); - if ((x->flags & 0x100U) && x->kind==FE_N_IDENT && + if(want && want->kind==FE_TYPE_SLICE && !want->ref_mut && + x->sem_type && x->sem_type->kind==FE_TYPE_SLICE && + x->sem_type->ref_mut) { + fputs(want->maker,e->out); fputc('(',e->out); + emit_expr(e,x); fputs(".p, ",e->out); + emit_expr(e,x); fputs(".n)",e->out); + } else if ((x->flags & 0x100U) && x->kind==FE_N_IDENT && x->sem_type && x->sem_type->kind==FE_TYPE_OWNED) { fputs("(fe_live_",e->out); fputs(cname(x,"owned"),e->out); fputs("=0, ",e->out); emit_expr(e,x); fputc(')',e->out); } else emit_expr(e, x); + if(call_param) call_param=call_param->next; } fputc(')', e->out); } diff --git a/fec/src/parser.c b/fec/src/parser.c index 9ddd72c..2e9366d 100644 --- a/fec/src/parser.c +++ b/fec/src/parser.c @@ -49,6 +49,7 @@ static FeNode *type(FeParser *p) if (is(p,FE_TOK_LBRACKET)) { next(p); n=toknode(p,FE_N_TYPE,t); if(!eat(p,FE_TOK_RBRACKET)) { n->a=expr(p,0); want(p,FE_TOK_RBRACKET,"expected ']' in array type"); } + else if(eat(p,FE_TOK_MUT)) n->text=fe_arena_strdup(&p->ast->arena,"[]mut",5); n->b=type(p); return n; } if (is(p,FE_TOK_FN)) { diff --git a/fec/src/types.c b/fec/src/types.c index f039b6d..c471dcf 100644 --- a/fec/src/types.c +++ b/fec/src/types.c @@ -64,7 +64,8 @@ FeType *fe_type_intern(FeTypeCtx *ctx, const char *name) if (strcmp(name, "void") == 0) kind = FE_TYPE_VOID; else if (strcmp(name, "bool") == 0) kind = FE_TYPE_BOOL; else if (strcmp(name, "char") == 0) kind = FE_TYPE_CHAR; - else if (strcmp(name, "str") == 0) kind = FE_TYPE_STR; + else if (strcmp(name, "str") == 0) + return fe_type_slice(ctx, fe_type_intern(ctx, "u8")); else if (strcmp(name, "io.Writer") == 0) { kind = FE_TYPE_STRUCT; } @@ -156,6 +157,26 @@ FeType *fe_type_slice(FeTypeCtx *ctx, FeType *elem) return t; } +FeType *fe_type_mut_slice(FeTypeCtx *ctx, FeType *elem) +{ + char key[96]; + FeType *t; + sprintf(key, "[]mut %s", elem ? elem->name : "?"); + t = fe_type_intern(ctx, key); + if (t->kind == FE_TYPE_UNKNOWN) { + t->kind = FE_TYPE_SLICE; + t->elem = elem; + t->ref_mut = 1; + t->cname = generated_name(ctx, "fe_mut_slice_", "type"); + t->maker = generated_name(ctx, "fe_make_mut_slice_", "type"); + t->indexer = generated_name(ctx, "fe_idx_mut_slice_", "type"); + t->slicer = generated_name(ctx, "fe_slice_mut_slice_", "type"); + t->full_slicer = generated_name(ctx, "fe_full_mut_slice_", "type"); + t->tail_slicer = generated_name(ctx, "fe_tail_mut_slice_", "type"); + } + return t; +} + FeType *fe_type_ref(FeTypeCtx *ctx, FeType *elem, int mutable) { char key[128]; @@ -478,7 +499,7 @@ FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node) if (node->text && strcmp(node->text, "as") == 0) return fe_type_from_ast(ctx, node->b); if (node->text && strcmp(node->text, "str") == 0) - return fe_type_intern(ctx, "str"); + return fe_type_slice(ctx, fe_type_intern(ctx, "u8")); if (node->a && node->a->kind==FE_N_IDENT && node->text && strcmp(node->text,"io")==0 && node->a->text) { sprintf(qualified,"%s.%s",node->text,node->a->text); @@ -490,13 +511,16 @@ FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node) strcmp(node->text,"&mut") == 0); if (node->text && strcmp(node->text,"^")==0) return fe_type_owned(ctx,fe_type_from_ast(ctx,node->a)); - if (node->text && strcmp(node->text, "[") == 0) { + if (node->text && (strcmp(node->text, "[") == 0 || + strcmp(node->text, "[]mut") == 0)) { if (node->a) { if (node->a->kind == FE_N_LITERAL && node->a->text) length = strtoul(node->a->text, 0, 0); return fe_type_array(ctx, length, fe_type_from_ast(ctx, node->b)); } - return fe_type_slice(ctx, fe_type_from_ast(ctx, node->b)); + return strcmp(node->text,"[]mut")==0 ? + fe_type_mut_slice(ctx, fe_type_from_ast(ctx,node->b)) : + fe_type_slice(ctx, fe_type_from_ast(ctx, node->b)); } if (node->text && strcmp(node->text, "!") == 0) /* Prefix !T stores T in a; the E!T spelling stores its success diff --git a/fec/src/types.h b/fec/src/types.h index 4acab3c..fae31ae 100644 --- a/fec/src/types.h +++ b/fec/src/types.h @@ -73,6 +73,7 @@ FeType *fe_type_intern(FeTypeCtx *ctx, const char *name); FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node); FeType *fe_type_array(FeTypeCtx *ctx, unsigned long length, FeType *elem); FeType *fe_type_slice(FeTypeCtx *ctx, FeType *elem); +FeType *fe_type_mut_slice(FeTypeCtx *ctx, FeType *elem); FeType *fe_type_ref(FeTypeCtx *ctx, FeType *elem, int mutable); FeType *fe_type_owned(FeTypeCtx *ctx, FeType *elem); FeType *fe_type_error_union(FeTypeCtx *ctx, FeType *value); diff --git a/fec/test-dos.bat b/fec/test-dos.bat index 70026fb..be85d12 100644 --- a/fec/test-dos.bat +++ b/fec/test-dos.bat @@ -15,9 +15,9 @@ fec.exe --dump-ast TESTS\PASS\BASIC.FE > nul if errorlevel 1 goto test_fail fec.exe --dump-ast TESTS\PASS\LITERALS.FE > nul if errorlevel 1 goto test_fail -fec.exe --dump-ast TESTS\PASS\KEYWORDS-AND-BUILTINS.FE > nul +fec.exe --dump-ast TESTS\PASS\KEYBUILT.FE > nul if errorlevel 1 goto test_fail -fec.exe --dump-ast TESTS\PASS\V012-FORMS.FE > nul +fec.exe --dump-ast TESTS\PASS\V012FORM.FE > nul if errorlevel 1 goto test_fail fec.exe --dump-ast STD\CORE.FE > nul @@ -37,11 +37,11 @@ if errorlevel 1 goto test_fail fec.exe --dump-ast STD\SYS.FE > nul if errorlevel 1 goto test_fail -fec.exe --dump-ast TESTS\FAIL\MISSING-SEMI.FE > nul +fec.exe --dump-ast TESTS\FAIL\MISSSEMI.FE > nul if not errorlevel 1 goto test_fail -fec.exe --dump-ast TESTS\FAIL\UNCLOSED-COMMENT.FE > nul +fec.exe --dump-ast TESTS\FAIL\UNCLCOMM.FE > nul if not errorlevel 1 goto test_fail -fec.exe --dump-ast TESTS\FAIL\LOGICAL-SYMBOLS.FE > nul +fec.exe --dump-ast TESTS\FAIL\LOGICAL.FE > nul if not errorlevel 1 goto test_fail if exist TESTS\M2\HELLO.C del TESTS\M2\HELLO.C @@ -67,28 +67,28 @@ TESTS\M2\SCOPES.EXE if errorlevel 1 goto test_fail rem M2 bits16 regression path remains on compiler A (wcl). -fec.exe --target=bits16 --emit-c TESTS\M2\CAST-WHILE.FE -o TESTS\M2\CAST16.C > nul +fec.exe --target=bits16 --emit-c TESTS\M2\CASTWHIL.FE -o TESTS\M2\CAST16.C > nul if errorlevel 1 goto test_fail wcl -q -za -bt=dos -fe=TESTS\M2\CAST16.EXE TESTS\M2\CAST16.C if errorlevel 1 goto test_fail TESTS\M2\CAST16.EXE if errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M2\BAD-CONDITION.FE -o TESTS\M2\BAD-CO.C > nul +fec.exe --target=bits32 --emit-c TESTS\M2\BAD-COND.FE -o TESTS\M2\BAD-CO.C > nul if not errorlevel 1 goto test_fail fec.exe --target=bits32 --emit-c TESTS\M2\BAD-CAST.FE -o TESTS\M2\BAD-CA.C > nul if not errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M2\BAD-ASSIGN.FE -o TESTS\M2\BAD-AS.C > nul +fec.exe --target=bits32 --emit-c TESTS\M2\BAD-ASGN.FE -o TESTS\M2\BAD-AS.C > nul if not errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M2\BAD-UNKNOWN.FE -o TESTS\M2\BAD-UN.C > nul +fec.exe --target=bits32 --emit-c TESTS\M2\BAD-UNK.FE -o TESTS\M2\BAD-UN.C > nul if not errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M2\BAD-ARITY.FE -o TESTS\M2\BAD-AR.C > nul +fec.exe --target=bits32 --emit-c TESTS\M2\BAD-ARI.FE -o TESTS\M2\BAD-AR.C > nul if not errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M2\BAD-TYPES.FE -o TESTS\M2\BAD-TY.C > nul +fec.exe --target=bits32 --emit-c TESTS\M2\BAD-TYPE.FE -o TESTS\M2\BAD-TY.C > nul if not errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M2\BAD-RETURN.FE -o TESTS\M2\BAD-RE.C > nul +fec.exe --target=bits32 --emit-c TESTS\M2\BAD-RET.FE -o TESTS\M2\BAD-RE.C > nul if not errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M2\BAD-UNINIT.FE -o TESTS\M2\BAD-UI.C > nul +fec.exe --target=bits32 --emit-c TESTS\M2\BAD-UNIT.FE -o TESTS\M2\BAD-UI.C > nul if not errorlevel 1 goto test_fail fec.exe --target=bits32 --emit-c TESTS\M2\BAD-VOID.FE -o TESTS\M2\BAD-VO.C > nul if not errorlevel 1 goto test_fail @@ -132,6 +132,16 @@ wcl386 -q -za -bt=dos -fe=TESTS\M3\ARRAY.EXE TESTS\M3\ARRAY.C if errorlevel 1 goto test_fail TESTS\M3\ARRAY.EXE if errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\MUTABLE.FE -o TESTS\M3\MUTABLE.C > nul +if errorlevel 1 goto test_fail +wcl386 -q -za -bt=dos -fe=TESTS\M3\MUTABLE.EXE TESTS\M3\MUTABLE.C +if errorlevel 1 goto test_fail +TESTS\M3\MUTABLE.EXE +if errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\BAD-MLET.FE -o TESTS\M3\BAD-MLET.C > nul +if not errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\BAD-SHWR.FE -o TESTS\M3\BAD-SHWR.C > nul +if not errorlevel 1 goto test_fail fec.exe --target=bits32 --emit-c TESTS\M3\STR.FE -o TESTS\M3\STR.C > nul if errorlevel 1 goto test_fail wcl386 -q -za -bt=dos -fe=TESTS\M3\STR.EXE TESTS\M3\STR.C @@ -168,6 +178,12 @@ wcl386 -q -za -bt=dos -fe=TESTS\M3\BOUNDS.EXE TESTS\M3\BOUNDS.C if errorlevel 1 goto test_fail TESTS\M3\BOUNDS.EXE if not errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M3\SLCBOUND.FE -o TESTS\M3\SLCBOUND.C > nul +if errorlevel 1 goto test_fail +wcl386 -q -za -bt=dos -fe=TESTS\M3\SLCBOUND.EXE TESTS\M3\SLCBOUND.C +if errorlevel 1 goto test_fail +TESTS\M3\SLCBOUND.EXE +if not errorlevel 1 goto test_fail fec.exe --target=bits32 --no-checks --emit-c TESTS\M3\BOUNDS.FE -o TESTS\M3\BOUNDS-N.C > nul if errorlevel 1 goto test_fail wcl386 -q -za -bt=dos -fe=TESTS\M3\BOUNDS-N.EXE TESTS\M3\BOUNDS-N.C @@ -197,7 +213,7 @@ wcl386 -q -za -wx -wcd=202 -bt=dos -fe=TESTS\M4\FORMAT.EXE TESTS\M4\FORMAT.C if errorlevel 1 goto test_fail TESTS\M4\FORMAT.EXE > nul if errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M4\TRY-FPRINT.FE -o TESTS\M4\TRY-FPR.C > nul +fec.exe --target=bits32 --emit-c TESTS\M4\TRY-FPR.FE -o TESTS\M4\TRY-FPR.C > nul if errorlevel 1 goto test_fail wcl386 -q -za -wx -wcd=202 -bt=dos -fe=TESTS\M4\TRY-FPR.EXE TESTS\M4\TRY-FPR.C if errorlevel 1 goto test_fail @@ -209,17 +225,17 @@ wcl386 -q -za -wx -wcd=202 -bt=dos -fe=TESTS\M4\PROP.EXE TESTS\M4\PROPTEST.C if errorlevel 1 goto test_fail TESTS\M4\PROP.EXE > nul if errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M4\BAD-ARITY.FE -o TESTS\M4\BAD-ARI.C > nul +fec.exe --target=bits32 --emit-c TESTS\M4\BAD-ARI.FE -o TESTS\M4\BAD-ARI.C > nul if not errorlevel 1 goto test_fail fec.exe --target=bits32 --emit-c TESTS\M4\BAD-VERB.FE -o TESTS\M4\BAD-VERB.C > nul if not errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M4\BAD-RUNTIME.FE -o TESTS\M4\BAD-RUN.C > nul +fec.exe --target=bits32 --emit-c TESTS\M4\BAD-RUN.FE -o TESTS\M4\BAD-RUN.C > nul if not errorlevel 1 goto test_fail fec.exe --target=bits32 --emit-c TESTS\M4\BAD-TYPE.FE -o TESTS\M4\BAD-TYP.C > nul if not errorlevel 1 goto test_fail fec.exe --target=bits32 --emit-c TESTS\M4\BAD-TRY.FE -o TESTS\M4\BAD-TRY.C > nul if not errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M4\BAD-WRITER.FE -o TESTS\M4\BAD-WRI.C > nul +fec.exe --target=bits32 --emit-c TESTS\M4\BAD-WRIT.FE -o TESTS\M4\BAD-WRI.C > nul if not errorlevel 1 goto test_fail fec.exe --target=bits32 --emit-c TESTS\M4\BAD-MANY.FE -o TESTS\M4\BAD-MANY.C > nul if not errorlevel 1 goto test_fail @@ -233,13 +249,13 @@ fec.exe --target=bits32 --emit-c TESTS\M5\OWNED.FE -o TESTS\M5\OWNED.C > nul if errorlevel 1 goto test_fail fec.exe --target=bits32 --emit-c TESTS\M5\BAD-MOVE.FE -o TESTS\M5\BAD-MOVE.C > nul if not errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M5\BAD-DESTROY.FE -o TESTS\M5\BAD-DES.C > nul +fec.exe --target=bits32 --emit-c TESTS\M5\BAD-DEST.FE -o TESTS\M5\BAD-DES.C > nul if not errorlevel 1 goto test_fail fec.exe --target=bits32 --emit-c TESTS\M5\BAD-DROP.FE -o TESTS\M5\BAD-DROP.C > nul if not errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M5\BAD-DOUBLE.FE -o TESTS\M5\BAD-DBL.C > nul +fec.exe --target=bits32 --emit-c TESTS\M5\BAD-DBL.FE -o TESTS\M5\BAD-DBL.C > nul if not errorlevel 1 goto test_fail -fec.exe --target=bits32 --emit-c TESTS\M5\BAD-CONDITIONAL.FE -o TESTS\M5\BAD-COND.C > nul +fec.exe --target=bits32 --emit-c TESTS\M5\BAD-COND.FE -o TESTS\M5\BAD-COND.C > nul if not errorlevel 1 goto test_fail if exist TESTS\M5\RUNTIME-G.C del TESTS\M5\RUNTIME-G.C if exist TESTS\M5\RUNTIME.O del TESTS\M5\RUNTIME.O diff --git a/fec/tests/fail/logical-symbols.fe b/fec/tests/fail/logical.fe similarity index 100% rename from fec/tests/fail/logical-symbols.fe rename to fec/tests/fail/logical.fe diff --git a/fec/tests/fail/missing-semi.fe b/fec/tests/fail/misssemi.fe similarity index 100% rename from fec/tests/fail/missing-semi.fe rename to fec/tests/fail/misssemi.fe diff --git a/fec/tests/fail/unclosed-comment.fe b/fec/tests/fail/unclcomm.fe similarity index 100% rename from fec/tests/fail/unclosed-comment.fe rename to fec/tests/fail/unclcomm.fe diff --git a/fec/tests/m2/bad-arity.fe b/fec/tests/m2/bad-ari.fe similarity index 100% rename from fec/tests/m2/bad-arity.fe rename to fec/tests/m2/bad-ari.fe diff --git a/fec/tests/m2/bad-assign.fe b/fec/tests/m2/bad-asgn.fe similarity index 100% rename from fec/tests/m2/bad-assign.fe rename to fec/tests/m2/bad-asgn.fe diff --git a/fec/tests/m2/bad-condition.fe b/fec/tests/m2/bad-cond.fe similarity index 100% rename from fec/tests/m2/bad-condition.fe rename to fec/tests/m2/bad-cond.fe diff --git a/fec/tests/m2/bad-return.fe b/fec/tests/m2/bad-ret.fe similarity index 100% rename from fec/tests/m2/bad-return.fe rename to fec/tests/m2/bad-ret.fe diff --git a/fec/tests/m2/bad-types.fe b/fec/tests/m2/bad-type.fe similarity index 100% rename from fec/tests/m2/bad-types.fe rename to fec/tests/m2/bad-type.fe diff --git a/fec/tests/m2/bad-uninit.fe b/fec/tests/m2/bad-unit.fe similarity index 100% rename from fec/tests/m2/bad-uninit.fe rename to fec/tests/m2/bad-unit.fe diff --git a/fec/tests/m2/bad-unknown.fe b/fec/tests/m2/bad-unk.fe similarity index 100% rename from fec/tests/m2/bad-unknown.fe rename to fec/tests/m2/bad-unk.fe diff --git a/fec/tests/m2/cast-while.fe b/fec/tests/m2/castwhil.fe similarity index 100% rename from fec/tests/m2/cast-while.fe rename to fec/tests/m2/castwhil.fe diff --git a/fec/tests/m3/bad-mlet.fe b/fec/tests/m3/bad-mlet.fe new file mode 100644 index 0000000..af257c1 --- /dev/null +++ b/fec/tests/m3/bad-mlet.fe @@ -0,0 +1,6 @@ +unit m3_bad_mut_let; + +fn bad() -> void { + var raw: [2]u8 = [1, 2]; + let s: []mut u8 = raw[..]; +} diff --git a/fec/tests/m3/bad-shwr.fe b/fec/tests/m3/bad-shwr.fe new file mode 100644 index 0000000..aa4f105 --- /dev/null +++ b/fec/tests/m3/bad-shwr.fe @@ -0,0 +1,5 @@ +unit m3_bad_shared_write; + +fn bad(s: []u8) -> void { + s[0] = 1; +} diff --git a/fec/tests/m3/mutable.fe b/fec/tests/m3/mutable.fe new file mode 100644 index 0000000..9c31d51 --- /dev/null +++ b/fec/tests/m3/mutable.fe @@ -0,0 +1,11 @@ +unit m3_mutable; + +fn takes_shared(s: []u8) -> u8 { return s[0]; } + +fn main() -> i32 { + var raw: [3]u8 = [1, 2, 3]; + var s: []mut u8 = raw[..]; + s[1] = 9; + if takes_shared(s) == 1 and raw[1] == 9 { return 0; } + return 1; +} diff --git a/fec/tests/m3/slcbound.fe b/fec/tests/m3/slcbound.fe new file mode 100644 index 0000000..89a8d88 --- /dev/null +++ b/fec/tests/m3/slcbound.fe @@ -0,0 +1,7 @@ +unit m3_slice_bounds; + +fn main() -> i32 { + let a: [2]i32 = [1, 2]; + let s: []i32 = a[0..3]; + return s.n as i32; +} diff --git a/fec/tests/m4/bad-arity.fe b/fec/tests/m4/bad-ari.fe similarity index 100% rename from fec/tests/m4/bad-arity.fe rename to fec/tests/m4/bad-ari.fe diff --git a/fec/tests/m4/bad-runtime.fe b/fec/tests/m4/bad-run.fe similarity index 100% rename from fec/tests/m4/bad-runtime.fe rename to fec/tests/m4/bad-run.fe diff --git a/fec/tests/m4/bad-writer.fe b/fec/tests/m4/bad-writ.fe similarity index 100% rename from fec/tests/m4/bad-writer.fe rename to fec/tests/m4/bad-writ.fe diff --git a/fec/tests/m4/try-fprint.fe b/fec/tests/m4/try-fpr.fe similarity index 100% rename from fec/tests/m4/try-fprint.fe rename to fec/tests/m4/try-fpr.fe diff --git a/fec/tests/m5/bad-conditional.fe b/fec/tests/m5/bad-cond.fe similarity index 100% rename from fec/tests/m5/bad-conditional.fe rename to fec/tests/m5/bad-cond.fe diff --git a/fec/tests/m5/bad-double.fe b/fec/tests/m5/bad-dbl.fe similarity index 100% rename from fec/tests/m5/bad-double.fe rename to fec/tests/m5/bad-dbl.fe diff --git a/fec/tests/m5/bad-destroy.fe b/fec/tests/m5/bad-dest.fe similarity index 100% rename from fec/tests/m5/bad-destroy.fe rename to fec/tests/m5/bad-dest.fe diff --git a/fec/tests/pass/keywords-and-builtins.fe b/fec/tests/pass/keybuilt.fe similarity index 100% rename from fec/tests/pass/keywords-and-builtins.fe rename to fec/tests/pass/keybuilt.fe diff --git a/fec/tests/pass/v012-forms.fe b/fec/tests/pass/v012form.fe similarity index 100% rename from fec/tests/pass/v012-forms.fe rename to fec/tests/pass/v012form.fe