harec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 71303937f5f98728d7b32bd418599b55fba43d70
parent fe176abcb22ea8d2342ea7e96e344aacd7443a83
Author: Bor Grošelj Simić <bor.groseljsimic@telemach.net>
Date:   Thu, 24 Mar 2022 01:44:24 +0100

fix null type handling

- null parses as a type only in match cases and in casts
- in type store, a distinction between type lookup calls that initiate
  type resolution and calls that are internal subtype resolutions is
  made so that it's possible to reject composite types that have null as
  one of its members.
- in check, error checks for bindings, globals and constants with type
  null are introduced

Fixes: https://todo.sr.ht/~sircmpwn/hare/580
Signed-off-by: Bor Grošelj Simić <bgs@turminal.net>

Diffstat:
Msrc/check.c | 15+++++++++++++++
Msrc/parse.c | 8+++++++-
Msrc/type_store.c | 52++++++++++++++++++++++++++++++++++------------------
Mtests/03-pointers.ha | 46++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 102 insertions(+), 19 deletions(-)

diff --git a/src/check.c b/src/check.c @@ -1018,6 +1018,11 @@ check_expr_binding(struct context *ctx, } } + if (type->storage == STORAGE_NULL) { + error(ctx, aexpr->loc, expr, + "Null is not a valid type for a binding"); + return; + } if (!type_is_assignable(type, initializer->result)) { error(ctx, aexpr->loc, expr, "Initializer is not assignable to binding type"); @@ -1789,6 +1794,11 @@ check_expr_match(struct context *ctx, "Cannot create binding for type of zero or undefined size"); return; } + if (ctype->storage == STORAGE_NULL) { + error(ctx, aexpr->loc, expr, + "Null is not a valid type for a binding"); + return; + } struct identifier ident = { .name = acase->name, }; @@ -3255,6 +3265,8 @@ scan_const(struct context *ctx, const struct ast_global_decl *decl) enum eval_result r = eval_expr(ctx, initializer, value); expect(&decl->init->loc, r == EVAL_OK, "Unable to evaluate constant initializer at compile time"); + expect(&decl->init->loc, type->storage != STORAGE_NULL, + "Null is not a valid type for a constant"); struct identifier ident = {0}; mkident(ctx, &ident, &decl->ident); @@ -3309,6 +3321,9 @@ scan_global(struct context *ctx, const struct ast_global_decl *decl) type = initializer->result; } + expect(&decl->init->loc, type->storage != STORAGE_NULL, + "Null is not a valid type for a global"); + struct identifier ident = {0}; if (decl->symbol) { ident.name = strdup(decl->symbol); diff --git a/src/parse.c b/src/parse.c @@ -1587,7 +1587,13 @@ parse_cast_expression(struct lexer *lexer, struct ast_expression *value) exp->type = EXPR_CAST; exp->cast.kind = kind; exp->cast.value = value; - exp->cast.type = parse_type(lexer); + if (lex(lexer, &tok) == T_NULL) { + exp->cast.type = mktype(&tok.loc); + exp->cast.type->storage = STORAGE_NULL; + } else { + unlex(lexer, &tok); + exp->cast.type = parse_type(lexer); + } return parse_cast_expression(lexer, exp); } diff --git a/src/type_store.c b/src/type_store.c @@ -18,9 +18,12 @@ const unsigned int typeflags[] = { TYPE_ERROR | TYPE_CONST, }; -static struct dimensions _type_store_lookup_atype(struct type_store *store, +static struct dimensions lookup_atype_with_dimensions(struct type_store *store, const struct type **type, const struct ast_type *atype); +static const struct type * +lookup_atype(struct type_store *store, const struct ast_type *atype); + static void error(struct context *ctx, const struct location loc, char *fmt, ...) { @@ -165,9 +168,9 @@ struct_insert_field(struct type_store *store, struct struct_field **fields, } struct dimensions dim = {0}; if (size_only) { - dim = _type_store_lookup_atype(store, NULL, atype->type); + dim = lookup_atype_with_dimensions(store, NULL, atype->type); } else { - dim = _type_store_lookup_atype(store, &field->type, atype->type); + dim = lookup_atype_with_dimensions(store, &field->type, atype->type); } if (dim.size == 0) { error(store->check_context, atype->type->loc, @@ -402,7 +405,7 @@ collect_atagged_memb(struct type_store *store, { for (; atu; atu = atu->next) { const struct type *type = - type_store_lookup_atype(store, atu->type); + lookup_atype(store, atu->type); if (type->storage == STORAGE_TAGGED) { collect_tagged_memb(store, ta, &type->tagged, i); continue; @@ -507,7 +510,7 @@ _tagged_size(struct type_store *store, const struct ast_tagged_union_type *u) } else if (atype->storage == STORAGE_TAGGED) { memb = _tagged_size(store, &atype->tagged_union); } else { - memb = _type_store_lookup_atype(store, NULL, atype); + memb = lookup_atype_with_dimensions(store, NULL, atype); } if (dim.size < memb.size) { dim.size = memb.size; @@ -547,7 +550,7 @@ tuple_init_from_atype(struct type_store *store, while (atuple) { struct dimensions memb = {0}; if (type) { - memb = _type_store_lookup_atype(store, &cur->type, atuple->type); + memb = lookup_atype_with_dimensions(store, &cur->type, atuple->type); if (memb.size == 0 || memb.align == 0) { error(store->check_context, atuple->type->loc, "Tuple member types must have nonzero size and alignment"); @@ -555,7 +558,7 @@ tuple_init_from_atype(struct type_store *store, } cur->offset = dim.size % memb.align + dim.size; } else { - memb = _type_store_lookup_atype(store, NULL, atuple->type); + memb = lookup_atype_with_dimensions(store, NULL, atuple->type); if (memb.size == 0 || memb.align == 0) { error(store->check_context, atuple->type->loc, "Tuple member types must have nonzero size and alignment"); @@ -615,7 +618,6 @@ type_init_from_atype(struct type_store *store, case STORAGE_I32: case STORAGE_I64: case STORAGE_INT: - case STORAGE_NULL: case STORAGE_RUNE: case STORAGE_SIZE: case STORAGE_STRING: @@ -677,10 +679,10 @@ type_init_from_atype(struct type_store *store, type->array.length = ast_array_len(store, atype); struct dimensions memb = {0}; if (size_only) { - memb = _type_store_lookup_atype(store, + memb = lookup_atype_with_dimensions(store, NULL, atype->array.members); } else { - memb = _type_store_lookup_atype(store, + memb = lookup_atype_with_dimensions(store, &type->array.members, atype->array.members); } if (memb.size == SIZE_UNDEFINED) { @@ -780,7 +782,7 @@ type_init_from_atype(struct type_store *store, if (size_only) { break; } - type->func.result = type_store_lookup_atype(store, + type->func.result = lookup_atype(store, atype->func.result); type->func.variadism = atype->func.variadism; type->func.flags = atype->func.flags; @@ -788,7 +790,7 @@ type_init_from_atype(struct type_store *store, for (struct ast_function_parameters *aparam = atype->func.params; aparam; aparam = aparam->next) { param = *next = xcalloc(1, sizeof(struct type_func_param)); - param->type = type_store_lookup_atype(store, aparam->type); + param->type = lookup_atype(store, aparam->type); if (atype->func.variadism == VARIADISM_HARE && !aparam->next) { param->type = type_store_lookup_slice( @@ -804,7 +806,7 @@ type_init_from_atype(struct type_store *store, break; } type->pointer.flags = atype->pointer.flags; - type->pointer.referent = type_store_lookup_atype( + type->pointer.referent = lookup_atype( store, atype->pointer.referent); break; case STORAGE_SLICE: @@ -813,7 +815,7 @@ type_init_from_atype(struct type_store *store, if (size_only) { break; } - type->array.members = type_store_lookup_atype( + type->array.members = lookup_atype( store, atype->array.members); type->array.length = SIZE_UNDEFINED; break; @@ -855,6 +857,11 @@ type_init_from_atype(struct type_store *store, tuple_init_from_atype(store, type, atype); } break; + case STORAGE_NULL: + error(store->check_context, atype->loc, + "Type null used in invalid context"); + *type = builtin_type_void; + return (struct dimensions){0}; } return (struct dimensions){ .size = type->size, .align = type->align }; } @@ -913,7 +920,7 @@ type_store_lookup_type(struct type_store *store, const struct type *type) } static struct dimensions -_type_store_lookup_atype(struct type_store *store, const struct type **type, const struct ast_type *atype) +lookup_atype_with_dimensions(struct type_store *store, const struct type **type, const struct ast_type *atype) { struct type temp = {0}; struct dimensions dim = {0}; @@ -927,15 +934,24 @@ _type_store_lookup_atype(struct type_store *store, const struct type **type, con return dim; } -const struct type * -type_store_lookup_atype(struct type_store *store, const struct ast_type *atype) +static const struct type * +lookup_atype(struct type_store *store, const struct ast_type *atype) { struct type temp = {0}; const struct type *type = &temp; - _type_store_lookup_atype(store, &type, atype); + lookup_atype_with_dimensions(store, &type, atype); return type; } +const struct type * +type_store_lookup_atype(struct type_store *store, const struct ast_type *atype) +{ + if (atype->storage == STORAGE_NULL) { + return &builtin_type_null; + }; + return lookup_atype(store, atype); +} + // Compute dimensions of an incomplete type without completing it struct dimensions type_store_lookup_dimensions(struct type_store *store, const struct ast_type *atype) diff --git a/tests/03-pointers.ha b/tests/03-pointers.ha @@ -22,7 +22,53 @@ fn _nullable() void = { ) != 0); }; +fn reject() void = { + assert(rt::compile(" + type s = null; + fn test() void = { + void; + }; + ") != 0); + assert(rt::compile(" + fn test() void = { + let a = &3: null; + }; + ") != 0); + assert(rt::compile(" + fn test() void = { + let b: nullable *int = null; + let a = b as null; + }; + ") != 0); + assert(rt::compile(" + fn test() void = { + let a = (null, 3); + }; + ") != 0); + assert(rt::compile(" + fn test() void = { + let a: []null = [null]; + }; + ") != 0); + assert(rt::compile(" + fn test() void = { + let a = [null]; + }; + ") != 0); + assert(rt::compile(" + fn test() void = { + let a: [_]null = [null]; + }; + ") != 0); + assert(rt::compile(" + fn test() void = { + let a = null; + }; + ") != 0); +}; + export fn main() void = { basics(); _nullable(); + reject(); };