From 6d5b83b7d85551e2cd08bc8daa001d913872ecbf Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Sat, 3 May 2025 11:31:26 +0200 Subject: [PATCH] compiler: extend cast syntax for initlists * parse compound cast expressions: depending on surrounding code, explicit casts can be more readable than implicit conversions as function arguments or return values. * add tests --- analyser/module_analyser_expr.c2 | 26 ++++++++- ast/explicit_cast_expr.c2 | 3 +- ast/expr.c2 | 2 +- generator/c/c2i_generator_expr.c2 | 3 +- generator/c/c_generator_expr.c2 | 3 +- generator/ir/ir_generator_expr.c2 | 4 ++ parser/c2_parser_expr.c2 | 12 ++-- .../explicit_cast/explicit_c_cast_array.c2 | 2 +- test/literals/compound_literals.c2 | 56 +++++++++++++++++++ 9 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 test/literals/compound_literals.c2 diff --git a/analyser/module_analyser_expr.c2 b/analyser/module_analyser_expr.c2 index 2b55780d..48b3e738 100644 --- a/analyser/module_analyser_expr.c2 +++ b/analyser/module_analyser_expr.c2 @@ -376,12 +376,36 @@ fn QualType Analyser.analyseExplicitCast(Analyser* ma, Expr** e_ptr) { TypeRef* ref = c.getTypeRef(); QualType destType = ma.analyseTypeRef(ref); + Expr* inner = c.getInner(); + if (inner.isInitList()) { + InitListExpr* initList = cast(inner); + if (destType.isInvalid()) + return QualType_Invalid; + if (destType.isArray() || destType.isStruct()) { + if (!ma.analyseInitListExpr(initList, destType)) + return QualType_Invalid; + } else { + u32 num_values = initList.getNumValues(); + Expr** values = initList.getValues(); + if (num_values != 1 || values[0].isInitList()) { + ma.error(inner.getLoc(), "invalid initializer"); + return QualType_Invalid; + } + if (!ma.analyseInitExpr(&values[0], destType, values[0].getLoc())) + return QualType_Invalid; + } + e.setLValue(); + c.setDestType(destType); + return destType; + } QualType srcType = ma.analyseExpr(c.getInner2(), true, RHS); if (srcType.isInvalid() || destType.isInvalid()) return QualType_Invalid; - Expr* inner = c.getInner(); + inner = c.getInner(); e.copyConstantFlags(inner); + // TODO: this is probably incorrect: valType should be LValue for reinterpret + // casts and RValue for conversion casts e.copyValType(inner); c.setDestType(destType); diff --git a/ast/explicit_cast_expr.c2 b/ast/explicit_cast_expr.c2 index 017316ec..08d2d463 100644 --- a/ast/explicit_cast_expr.c2 +++ b/ast/explicit_cast_expr.c2 @@ -39,6 +39,7 @@ public fn ExplicitCastExpr* ExplicitCastExpr.create(ast_context.Context* c, { u32 size = sizeof(ExplicitCastExpr) + ref.getExtraSize(); ExplicitCastExpr* e = c.alloc(size); + // TODO ValType.LValue for compound e.base.init(ExprKind.ExplicitCast, loc, 0, 0, 0, ValType.NValue); e.base.base.explicitCastExprBits.c_style = c_style; e.base.base.explicitCastExprBits.src_len = src_len; @@ -81,7 +82,7 @@ fn SrcLoc ExplicitCastExpr.getEndLoc(const ExplicitCastExpr* e) { return e.base.base.loc + e.base.base.explicitCastExprBits.src_len; } -fn void ExplicitCastExpr.printLiteral(const ExplicitCastExpr* e, string_buffer.Buf* out) { +public fn void ExplicitCastExpr.printLiteral(const ExplicitCastExpr* e, string_buffer.Buf* out) { if (e.base.base.explicitCastExprBits.c_style) { out.add1('('); e.dest.print(out, true); diff --git a/ast/expr.c2 b/ast/expr.c2 index 27bca512..203c81db 100644 --- a/ast/expr.c2 +++ b/ast/expr.c2 @@ -74,7 +74,7 @@ static_assert(elemsof(ExprKind), elemsof(exprKind_names)); /* An LValue may be on the left/right side of an assignment - An RValue may only be on the left side + An RValue may only be on the right side An NValue is an abstract object (cannot be used on either side) Lvalue: diff --git a/generator/c/c2i_generator_expr.c2 b/generator/c/c2i_generator_expr.c2 index b2cc5141..ef9dc7f4 100644 --- a/generator/c/c2i_generator_expr.c2 +++ b/generator/c/c2i_generator_expr.c2 @@ -106,7 +106,8 @@ fn void Generator.emitExpr(Generator* gen, const Expr* e) { case BitOffset: break; case ExplicitCast: - break; + ExplicitCastExpr.printLiteral(cast(e), out); + return; case ImplicitCast: const ImplicitCastExpr* c = cast(e); gen.emitExpr(c.getInner()); diff --git a/generator/c/c_generator_expr.c2 b/generator/c/c_generator_expr.c2 index 66951ac4..349e679e 100644 --- a/generator/c/c_generator_expr.c2 +++ b/generator/c/c_generator_expr.c2 @@ -132,6 +132,7 @@ fn void Generator.emitExpr(Generator* gen, string_buffer.Buf* out, Expr* e) { ExplicitCastExpr* c = cast(e); out.add("(("); gen.emitTypePre(out, c.getDestType()); + gen.emitTypePost(out, c.getDestType()); if (c.getCStyle()) { out.add1(')'); gen.emitExpr(out, c.getInner()); @@ -151,7 +152,7 @@ fn void Generator.emitExpr(Generator* gen, string_buffer.Buf* out, Expr* e) { gen.emitExpr(out, b.getLHS()); out.print(" ... "); gen.emitExpr(out, b.getRHS()); - return; + break; } } diff --git a/generator/ir/ir_generator_expr.c2 b/generator/ir/ir_generator_expr.c2 index f8083498..a63c16a8 100644 --- a/generator/ir/ir_generator_expr.c2 +++ b/generator/ir/ir_generator_expr.c2 @@ -90,6 +90,10 @@ fn void Generator.emitExpr(Generator* gen, ir.Ref* result, const Expr* e) { break; case ExplicitCast: ExplicitCastExpr* ec = cast(e); + if (ec.getInner().isInitList()) { + assert(0); // TODO + break; + } gen.emitExpr(result, ec.getInner()); break; case ImplicitCast: diff --git a/parser/c2_parser_expr.c2 b/parser/c2_parser_expr.c2 index 140ae78b..7afde3d7 100644 --- a/parser/c2_parser_expr.c2 +++ b/parser/c2_parser_expr.c2 @@ -527,16 +527,18 @@ fn Expr* Parser.parseParenExpr(Parser* p) { TypeRefHolder ref.init(); p.parseTypeSpecifier(&ref, true, has_brackets); p.expectAndConsume(Kind.RParen); + Expr* expr; if (p.tok.kind == Kind.LBrace) { // compound literal - p.error("Compound literals are not supported"); + expr = p.parseInitList(); } else { // C cast expression - if (has_brackets) p.error("array types are not allowed here"); - Expr* expr = p.parseCastExpr(false, false); - u32 src_len = p.prev_loc - loc; - return p.builder.actOnExplicitCast(loc, src_len, &ref, expr, true); + if (has_brackets) + p.error("cast to array type is invalid"); + expr = p.parseCastExpr(false, false); } + u32 src_len = p.prev_loc - loc; + return p.builder.actOnExplicitCast(loc, src_len, &ref, expr, true); } Expr* res = p.parseExpr(); diff --git a/test/expr/explicit_cast/explicit_c_cast_array.c2 b/test/expr/explicit_cast/explicit_c_cast_array.c2 index 52e96a71..cdc2ed54 100644 --- a/test/expr/explicit_cast/explicit_c_cast_array.c2 +++ b/test/expr/explicit_cast/explicit_c_cast_array.c2 @@ -2,6 +2,6 @@ module test; fn void test1a(i32 a) { - char[2] b1 = (char[2])(a); // @error{array types are not allowed here} + char[2] b1 = (char[2])(a); // @error{cast to array type is invalid} } diff --git a/test/literals/compound_literals.c2 b/test/literals/compound_literals.c2 new file mode 100644 index 00000000..c8fa0aee --- /dev/null +++ b/test/literals/compound_literals.c2 @@ -0,0 +1,56 @@ +// @warnings{no-unused} +module test; + +import stdio local; + +type Foo struct { + i32 x; + i32 y; +} + +fn void test_foo(Foo foo) {} +fn void Foo.bar(Foo* foo) {} +fn Foo Foo.create(i32 x, i32 y) { return (Foo){ x, y }; } + +fn i32 sum(i32 *p, u32 count) { + i32 total = 0; + for (i32 i = 0; i < count; i++) total += p[i]; + return total; +} + +public fn i32 main() { + Foo[] foos = { + { }, + { 0, 1 }, + (Foo){ 1, 2 }, + (Foo){ .x = 2, .y = 3 }, + [4] = { }, + [5] = { 3, 4 }, + [6] = (Foo){ 4, 5 }, + [7] = (Foo){ .x = 5, .y = 6 }, + }; + + test_foo({}); + test_foo({ 1, 2 }); + test_foo({ .x = 1, .y = 2 }); + test_foo((Foo){}); + test_foo((Foo){ 1, 2 }); + test_foo((Foo){ .x = 1, .y = 2 }); + + foos[0].bar(); + (Foo){1, 2}.bar(); + + printf("%d\n", sum((i32[]){ 1 }, 1)); + printf("%d\n", sum((i32[]){ 1, 2 }, 2)); + printf("%d\n", sum((i32[]){ 1, 2, 3 }, 3)); + printf("%d\n", sum((i32[1]){ 1 }, 1)); + printf("%d\n", sum((i32[2]){ 1, 2 }, 2)); + printf("%d\n", sum((i32[10]){ 1, 2, 3 }, 10)); + + // Enable these tests once arrays are implied for pointer arguments + //printf("%d\n", sum({ 1 }, 1)); + //printf("%d\n", sum({ 1, 2 }, 2)); + //printf("%d\n", sum({ 1, 2, 3 }, 3)); + + return 0; +}