diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2025-12-11 13:50:01 -0500 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2025-12-11 13:52:46 -0500 |
| commit | 7f8f2117799cdfa6b62909a9182b5adade1d0bd2 (patch) | |
| tree | 1db466db870768e952f50572453660e090e434e0 /src/compile/optionals.c | |
| parent | 630f910563b6f27dd34a4a0496a43d32539eadcb (diff) | |
| parent | 02886fab651d3f64d2c8ded5597e6c075dc69b5f (diff) | |
Merge branch 'dev' into constructive-reals
Diffstat (limited to 'src/compile/optionals.c')
| -rw-r--r-- | src/compile/optionals.c | 69 |
1 files changed, 49 insertions, 20 deletions
diff --git a/src/compile/optionals.c b/src/compile/optionals.c index 7da50b0b..9aca84d0 100644 --- a/src/compile/optionals.c +++ b/src/compile/optionals.c @@ -15,18 +15,14 @@ Text_t optional_into_nonnone(type_t *t, Text_t value) { switch (t->tag) { case IntType: case ByteType: return Texts(value, ".value"); - case StructType: - if (t == PATH_TYPE || t == PATH_TYPE_TYPE) return value; - return Texts(value, ".value"); + case StructType: return Texts(value, ".value"); default: return value; } } public Text_t promote_to_optional(type_t *t, Text_t code) { - if (t == PATH_TYPE || t == PATH_TYPE_TYPE) { - return code; - } else if (t->tag == IntType) { + if (t->tag == IntType) { switch (Match(t, IntType)->bits) { case TYPE_IBITS8: return Texts("((OptionalInt8_t){.has_value=true, .value=", code, "})"); case TYPE_IBITS16: return Texts("((OptionalInt16_t){.has_value=true, .value=", code, "})"); @@ -53,7 +49,6 @@ Text_t compile_none(type_t *t) { if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type"); if (t == PATH_TYPE) return Text("NONE_PATH"); - else if (t == PATH_TYPE_TYPE) return Text("PATHTYPE_NONE"); switch (t->tag) { case BigIntType: return Text("NONE_INT"); @@ -92,8 +87,6 @@ Text_t check_none(type_t *t, Text_t value) { // NOTE: these use statement expressions ({...;}) because some compilers // complain about excessive parens around equality comparisons if (t->tag == PointerType || t->tag == FunctionType || t->tag == CStringType) return Texts("(", value, " == NULL)"); - else if (t == PATH_TYPE) return Texts("((", value, ").type.$tag == PATHTYPE_NONE)"); - else if (t == PATH_TYPE_TYPE) return Texts("((", value, ").$tag == PATHTYPE_NONE)"); else if (t->tag == BigIntType) return Texts("((", value, ").small == 0)"); else if (t->tag == ClosureType) return Texts("((", value, ").fn == NULL)"); else if (t->tag == FloatType) @@ -103,10 +96,7 @@ Text_t check_none(type_t *t, Text_t value) { else if (t->tag == BoolType) return Texts("((", value, ") == NONE_BOOL)"); else if (t->tag == TextType) return Texts("((", value, ").tag == TEXT_NONE)"); else if (t->tag == IntType || t->tag == ByteType || t->tag == StructType) return Texts("!(", value, ").has_value"); - else if (t->tag == EnumType) { - if (enum_has_fields(t)) return Texts("((", value, ").$tag == 0)"); - else return Texts("((", value, ") == 0)"); - } + else if (t->tag == EnumType) return Texts("((", value, ").$tag == 0)"); print_err("Optional check not implemented for: ", type_to_text(t)); return EMPTY_TEXT; } @@ -115,12 +105,51 @@ public Text_t compile_non_optional(env_t *env, ast_t *ast) { ast_t *value = Match(ast, NonOptional)->value; if (value->tag == Index && Match(value, Index)->index != NULL) return compile_indexing(env, value, true); - type_t *t = get_type(env, value); - Text_t value_code = compile(env, value); + type_t *value_t = get_type(env, value); + if (value_t->tag == PointerType) { + // Dereference pointers automatically + return compile_non_optional(env, WrapAST(ast, NonOptional, WrapAST(ast, Index, .indexed = value))); + } int64_t line = get_line_number(ast->file, ast->start); - return Texts( - "({ ", compile_declaration(t, Text("opt")), " = ", value_code, "; ", "if unlikely (", - check_none(t, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(", quoted_str(ast->file->filename), ", ", - (int64_t)(value->start - value->file->text), ", ", (int64_t)(value->end - value->file->text), ", ", - "\"This was expected to be a value, but it's `none`\\n\");\n", optional_into_nonnone(t, Text("opt")), "; })"); + if (value_t->tag == EnumType) { + // For this case: + // enum Foo(FirstField, SecondField(msg:Text)) + // e := ... + // e! + // We desugar into `e.FirstField!` using the first enum field + tag_t *first_tag = Match(value_t, EnumType)->tags; + if (!first_tag) code_err(ast, "'!' cannot be used on an empty enum"); + return compile_non_optional( + env, WrapAST(ast, NonOptional, WrapAST(value, FieldAccess, .fielded = value, .field = first_tag->name))); + } else if (value->tag == FieldAccess + && value_type(get_type(env, Match(value, FieldAccess)->fielded))->tag == EnumType) { + type_t *enum_t = value_type(get_type(env, Match(value, FieldAccess)->fielded)); + DeclareMatch(e, enum_t, EnumType); + DeclareMatch(f, value, FieldAccess); + for (tag_t *tag = e->tags; tag; tag = tag->next) { + if (streq(f->field, tag->name)) { + Text_t tag_name = namespace_name(e->env, e->env->namespace, Texts("tag$", tag->name)); + return Texts( + "({ ", compile_declaration(enum_t, Text("_test_enum")), " = ", + compile_to_pointer_depth(env, f->fielded, 0, true), ";", + "if unlikely (_test_enum.$tag != ", tag_name, ") {\n", "#line ", line, "\n", "fail_source(", + quoted_str(f->fielded->file->filename), ", ", (int64_t)(f->fielded->start - f->fielded->file->text), + ", ", (int64_t)(f->fielded->end - f->fielded->file->text), ", ", "\"This was expected to be ", + tag->name, ", but it was: \", ", expr_as_text(Text("_test_enum"), enum_t, Text("false")), + ", \"\\n\");\n}\n", + compile_maybe_incref( + env, WrapLiteralCode(value, Texts("_test_enum.", tag->name), .type = tag->type), tag->type), + "; })"); + } + } + code_err(ast, "The field '", f->field, "' is not a valid tag name of ", type_to_text(enum_t)); + } else { + Text_t value_code = compile(env, value); + return Texts("({ ", compile_declaration(value_t, Text("opt")), " = ", value_code, "; ", "if unlikely (", + check_none(value_t, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(", + quoted_str(value->file->filename), ", ", (int64_t)(value->start - value->file->text), ", ", + (int64_t)(value->end - value->file->text), ", ", + "\"This was expected to be a value, but it's `none`\\n\");\n", + optional_into_nonnone(value_t, Text("opt")), "; })"); + } } |
