Improved error messages for initializers. Array initializers work.

This commit is contained in:
Christoffer Lerno
2020-04-13 22:21:49 +02:00
committed by Christoffer Lerno
parent 1d73338fb0
commit ff31bd17c0
6 changed files with 197 additions and 92 deletions

View File

@@ -25,8 +25,6 @@ There are some small work being done on the parser here, but most of the structu
#### What's missing in the parser #### What's missing in the parser
- `asm` sections. - `asm` sections.
- Macro parameter lists to imports.
- auxiliary data for enums.
- Docs not linked to statements/functions/declarations. - Docs not linked to statements/functions/declarations.
#### What's missing in the semantic analyser #### What's missing in the semantic analyser
@@ -46,17 +44,18 @@ There are some small work being done on the parser here, but most of the structu
- `type` not handled. - `type` not handled.
- Identifier analysis incomplete. - Identifier analysis incomplete.
- Macro call not handled completely. - Macro call not handled completely.
- Function calls not handled completely.
- Struct initializers not complete.
- Varargs.
#### What's missing overall #### What's missing overall
- Integration with C. - Improved integration with C.
- Generic macros.
#### What's working? #### What's working?
- Lexing and parsing works (except for the exceptions noted above). - Lexing and parsing works (except for the exceptions noted above).
- Simple "hello world" - Simple "hello world"
- Most simpler C code.
(For more details see missing.txt)
If you wish to contribute with ideas, please file issues on the c3docs: https://github.com/c3lang/c3docs instead of the compiler. If you wish to contribute with ideas, please file issues on the c3docs: https://github.com/c3lang/c3docs instead of the compiler.

View File

@@ -12,13 +12,12 @@ Things missing:
- Labels: @unused - Labels: @unused
* Designated initializer * Designated initializer
- Array initializer
- Array range initializer { [1..2] = 2 } - Array range initializer { [1..2] = 2 }
* Initializers * Initializers
- Array initializers - Vararray initializers
- Union initializers - Incremental array initializers
- Initializers with anonymous members - Slice initializers
* Asserts * Asserts
- assert, $assert - assert, $assert

View File

@@ -255,6 +255,7 @@ func int barok() throws Error, OtherError
return 100; return 100;
} }
struct SimpleStruct struct SimpleStruct
{ {
int a; int a;
@@ -283,7 +284,7 @@ func void testSimpleStruct(int x)
struct AnonStruct struct AnonStruct
{ {
int a; int a;
struct sune struct xx
{ {
int b; int b;
int c; int c;
@@ -301,22 +302,50 @@ struct AnonStruct
int x; int x;
} }
func void testAnonStruct() func void testAnonStruct2()
{ {
AnonStruct s = { b2 = 3, b1 = 7, sune.b = 1 }; AnonStruct s = { b2 = 3, b1 = 7, xx.b = 1 };
AnonStruct foo; AnonStruct foo;
printf("a = %d, b = %d (1), c = %d, b1 = %d (7), c1 = %d, b2 = %d (3), c2 = %d (3), x = %d\n", s.a, s.sune.b, s.sune.c, s.b1, s.c1, s.b2, s.c2, s.x); printf("a = %d, b = %d (1), c = %d, b1 = %d (7), c1 = %d, b2 = %d (3), c2 = %d (3), x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x);
s.sune.b = 100; s.xx.b = 100;
s.sune.c = 99; s.xx.c = 99;
s.b1 = 3; s.b1 = 3;
s.b2 = 5; s.b2 = 5;
s.c2 = 7; s.c2 = 7;
printf("a = %d, b = %d (100), c = %d (99), b1 = %d (3), c1 = %d, b2 = %d (7), c2 = %d (7), x = %d\n", s.a, s.sune.b, s.sune.c, s.b1, s.c1, s.b2, s.c2, s.x); printf("a = %d, b = %d (100), c = %d (99), b1 = %d (3), c1 = %d, b2 = %d (7), c2 = %d (7), x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x);
}
func void testAnonStruct()
{
AnonStruct s = { b2 = 3, b1 = 7, xx.b = 1 };
AnonStruct foo;
printf("a = %d, b = %d (1), c = %d, b1 = %d (7), c1 = %d, b2 = %d (3), c2 = %d (3), x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x);
s.xx.b = 100;
s.xx.c = 99;
s.b1 = 3;
s.b2 = 5;
s.c2 = 7;
printf("a = %d, b = %d (100), c = %d (99), b1 = %d (3), c1 = %d, b2 = %d (7), c2 = %d (7), x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x);
s = { xx = { c = 2 }};
printf("a = %d, b = %d, c = %d (2), b1 = %d, c1 = %d, b2 = %d, c2 = %d, x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x);
s.xx.c = 3;
s.x = 1212;
s.a = 29183;
s = AnonStruct { xx = { c = 2 }};
printf("a = %d, b = %d, c = %d (2), b1 = %d, c1 = %d, b2 = %d, c2 = %d, x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x);
} }
func int boba(int y, int j) func int boba(int y, int j)
{ {
@@ -561,7 +590,8 @@ struct WithArray
func void testArray() func void testArray()
{ {
//int[4] zebra = { [0] = 1 }; int[4] zebra = { [2] = 1 };
int[4] deok = { 1, 2, 3, 4 };
WithArray boo; WithArray boo;
boo.x[0] = 2; boo.x[0] = 2;
printf("boo.x[0] = %d\n", boo.x[0]); printf("boo.x[0] = %d\n", boo.x[0]);

View File

@@ -136,7 +136,7 @@ typedef struct _Path
typedef enum typedef enum
{ {
DESIGNATED_IDENT, DESIGNATED_IDENT,
DESIGNATED_SUBSCRIPT DESIGNATED_SUBSCRIPT,
} DesignatedPathKind; } DesignatedPathKind;
typedef struct _DesignatedPath typedef struct _DesignatedPath
@@ -150,6 +150,7 @@ typedef struct _DesignatedPath
Expr *index_expr; Expr *index_expr;
}; };
} DesignatedPath; } DesignatedPath;
typedef struct typedef struct
{ {
unsigned char bitsize; unsigned char bitsize;

View File

@@ -76,10 +76,13 @@ bool parse_param_list(Context *context, Expr ***result, bool allow_type)
Expr *expr = NULL; Expr *expr = NULL;
SourceRange start = context->tok.span; SourceRange start = context->tok.span;
// Special handling of [123] // Special handling of [123]
if (try_consume(context, TOKEN_LBRACKET)) if (context->tok.type == TOKEN_LBRACKET)
{ {
expr = TRY_EXPR_OR(parse_expr(context), false); expr = expr_new(EXPR_SUBSCRIPT, context->tok.span);
advance_and_verify(context, TOKEN_LBRACKET);
expr->subscript_expr.index = TRY_EXPR_OR(parse_expr(context), false);
CONSUME_OR(TOKEN_RBRACKET, false); CONSUME_OR(TOKEN_RBRACKET, false);
RANGE_EXTEND_PREV(expr);
expr = TRY_EXPR_OR(parse_precedence_with_left_side(context, expr, PREC_ASSIGNMENT), false); expr = TRY_EXPR_OR(parse_precedence_with_left_side(context, expr, PREC_ASSIGNMENT), false);
} }
else else

View File

@@ -357,24 +357,11 @@ static inline bool sema_expr_analyse_subscript_after_parent_resolution(Context *
Expr *subscripted = expr->subscript_expr.expr; Expr *subscripted = expr->subscript_expr.expr;
Type *type = parent ? parent->canonical : subscripted->type->canonical; Type *type = parent ? parent->canonical : subscripted->type->canonical;
Expr *index = expr->subscript_expr.index; Expr *index = expr->subscript_expr.index;
Type *inner_type; Type *inner_type = type_get_indexed_type(type);
switch (type->type_kind) if (!inner_type)
{ {
case TYPE_POINTER: SEMA_ERROR((parent ? expr : subscripted), "Cannot index '%s'.", type_to_error_string(type));
inner_type = type->pointer; return false;
break;
case TYPE_VARARRAY:
case TYPE_ARRAY:
inner_type = type->array.base;
break;
case TYPE_SUBARRAY:
TODO
case TYPE_STRING:
inner_type = type_char;
break;
default:
SEMA_ERROR((parent ? expr : subscripted), "Cannot index '%s'.", type_to_error_string(type));
return false;
} }
if (!sema_analyse_expr(context, type_isize, index)) return false; if (!sema_analyse_expr(context, type_isize, index)) return false;
@@ -575,9 +562,9 @@ static inline bool sema_expr_analyse_type_access(Context *context, Type *to, Exp
return false; return false;
} }
static DesignatedPath *sema_analyse_init_path(Context *context, DesignatedPath *path, Expr *expr); static DesignatedPath *sema_analyse_init_path(Context *context, DesignatedPath *parent, Expr *expr, bool *has_found_match, bool *has_reported_error);
static DesignatedPath *sema_analyse_init_identifier_string(Context *context, DesignatedPath *parent_path, const char *string) static DesignatedPath *sema_analyse_init_identifier_string(Context *context, DesignatedPath *parent_path, const char *string, bool *has_found_match, bool *has_reported_error)
{ {
assert(type_is_structlike(parent_path->type)); assert(type_is_structlike(parent_path->type));
Decl **members = parent_path->type->decl->strukt.members; Decl **members = parent_path->type->decl->strukt.members;
@@ -591,19 +578,21 @@ static DesignatedPath *sema_analyse_init_identifier_string(Context *context, Des
sub_path->kind = DESIGNATED_IDENT; sub_path->kind = DESIGNATED_IDENT;
sub_path->index = i; sub_path->index = i;
parent_path->sub_path = sub_path; parent_path->sub_path = sub_path;
*has_found_match = true;
return sub_path; return sub_path;
} }
if (!member->name) if (!member->name)
{ {
DesignatedPath temp_path; DesignatedPath temp_path;
temp_path.type = member->type; temp_path.type = member->type;
DesignatedPath *found = sema_analyse_init_identifier_string(context, &temp_path, string); DesignatedPath *found = sema_analyse_init_identifier_string(context, &temp_path, string, has_found_match, has_reported_error);
if (!found) continue; if (!found) continue;
DesignatedPath *real_path = malloc_arena(sizeof(DesignatedPath)); DesignatedPath *real_path = malloc_arena(sizeof(DesignatedPath));
*real_path = temp_path; *real_path = temp_path;
real_path->index = i; real_path->index = i;
real_path->kind = DESIGNATED_IDENT; real_path->kind = DESIGNATED_IDENT;
parent_path->sub_path = real_path; parent_path->sub_path = real_path;
*has_found_match = true;
return found; return found;
} }
} }
@@ -611,11 +600,17 @@ static DesignatedPath *sema_analyse_init_identifier_string(Context *context, Des
} }
static DesignatedPath *sema_analyse_init_access(Context *context, DesignatedPath *parent, Expr *access_expr) static DesignatedPath *sema_analyse_init_access(Context *context, DesignatedPath *parent, Expr *access_expr, bool *has_found_match, bool *has_reported_error)
{ {
DesignatedPath *last_path = sema_analyse_init_path(context, parent, access_expr->access_expr.parent); DesignatedPath *last_path = sema_analyse_init_path(context, parent, access_expr->access_expr.parent, has_found_match, has_reported_error);
if (!last_path) return NULL; if (!last_path) return NULL;
return sema_analyse_init_identifier_string(context, last_path, access_expr->access_expr.sub_element.string); DesignatedPath *path = sema_analyse_init_identifier_string(context, last_path, access_expr->access_expr.sub_element.string, has_found_match, has_reported_error);
if (!path && has_found_match && !has_reported_error)
{
SEMA_TOKEN_ERROR(access_expr->access_expr.sub_element, "'%s' is not a valid sub member.", access_expr->access_expr.sub_element.string);
*has_reported_error = true;
}
return path;
} }
static bool expr_cast_to_index(Expr *index) static bool expr_cast_to_index(Expr *index)
@@ -667,17 +662,22 @@ static bool expr_check_index_in_range(Type *type, Expr *index)
} }
return true; return true;
} }
static DesignatedPath *sema_analyse_init_subscript(Context *context, DesignatedPath *parent, Expr *expr) static DesignatedPath *sema_analyse_init_subscript(Context *context, DesignatedPath *parent, Expr *expr, bool *has_found_match, bool *has_reported_error)
{ {
assert(expr->expr_kind == EXPR_SUBSCRIPT); assert(expr->expr_kind == EXPR_SUBSCRIPT);
DesignatedPath *path = sema_analyse_init_path(context, parent, expr->subscript_expr.expr); DesignatedPath *path = parent;
if (expr->subscript_expr.expr)
{
path = sema_analyse_init_path(context, parent, expr->subscript_expr.expr, has_found_match, has_reported_error);
}
if (!path) return NULL; if (!path) return NULL;
Type *type = path->type; Type *type = path->type;
if (type->canonical->type_kind == TYPE_POINTER) if (type->canonical->type_kind == TYPE_POINTER)
{ {
SEMA_ERROR(expr, "It's not possible to subscript a pointer field in a designated initializer."); SEMA_ERROR(expr, "It's not possible to subscript a pointer field in a designated initializer.");
return false; *has_reported_error = true;
return NULL;
} }
Expr *index = expr->subscript_expr.index; Expr *index = expr->subscript_expr.index;
@@ -685,79 +685,82 @@ static DesignatedPath *sema_analyse_init_subscript(Context *context, DesignatedP
if (!inner_type) if (!inner_type)
{ {
SEMA_ERROR(expr, "Not possible to index a value of type '%s'.", type_to_error_string(type)); SEMA_ERROR(expr, "Not possible to index a value of type '%s'.", type_to_error_string(type));
return false; *has_reported_error = true;
return NULL;
}
if (!sema_analyse_expr(context, type_isize, index))
{
*has_reported_error = true;
return NULL;
} }
if (!sema_analyse_expr(context, type_isize, index)) return false;
// Unless we already have type_usize, cast to type_isize; // Unless we already have type_usize, cast to type_isize;
if (!expr_cast_to_index(index)) return false; if (!expr_cast_to_index(index))
{
*has_reported_error = true;
return NULL;
}
// Check range // Check range
if (!expr_check_index_in_range(type->canonical, index)) return false; if (!expr_check_index_in_range(type->canonical, index))
{
*has_reported_error = true;
return NULL;
}
DesignatedPath *sub_path = CALLOCS(DesignatedPath); DesignatedPath *sub_path = CALLOCS(DesignatedPath);
path->sub_path = sub_path; path->sub_path = sub_path;
sub_path->type = inner_type; sub_path->type = inner_type;
sub_path->kind = DESIGNATED_SUBSCRIPT; sub_path->kind = DESIGNATED_SUBSCRIPT;
sub_path->index_expr = index; sub_path->index_expr = index;
*has_found_match = true;
return sub_path; return sub_path;
} }
static DesignatedPath *sema_analyse_init_path(Context *context, DesignatedPath *parent, Expr *expr) static DesignatedPath *sema_analyse_init_path(Context *context, DesignatedPath *parent, Expr *expr, bool *has_found_match, bool *has_reported_error)
{ {
switch (expr->expr_kind) switch (expr->expr_kind)
{ {
case EXPR_ACCESS: case EXPR_ACCESS:
return sema_analyse_init_access(context, parent, expr); return sema_analyse_init_access(context, parent, expr, has_found_match, has_reported_error);
case EXPR_IDENTIFIER: case EXPR_IDENTIFIER:
return sema_analyse_init_identifier_string(context, parent, expr->identifier_expr.identifier); return sema_analyse_init_identifier_string(context, parent, expr->identifier_expr.identifier, has_found_match, has_reported_error);
case EXPR_SUBSCRIPT: case EXPR_SUBSCRIPT:
return sema_analyse_init_subscript(context, parent, expr); return sema_analyse_init_subscript(context, parent, expr, has_found_match, has_reported_error);
default: default:
return NULL; return NULL;
} }
} }
/**
* Recursively find a node in a declaration.
* @return NULL if it wasn't found, otherwise the member.
*/
DesignatedInitializer *find_initializer(Decl *decl, DesignatedInitializer *initializer, const char* name)
{
Decl** compare_members = decl->strukt.members;
VECEACH(compare_members, i)
{
Decl *member = compare_members[i];
if (!member->name)
{
DesignatedInitializer *sub_initializer = initializer->initializers[i];
DesignatedInitializer *found = find_initializer(member, sub_initializer, name);
if (found) return found;
}
else if (member->name == name) return initializer;
}
return NULL;
}
static bool sema_expr_analyse_struct_designated_initializer(Context *context, Type *assigned, Expr *initializer) static bool sema_expr_analyse_designated_initializer(Context *context, Type *assigned, Expr *initializer)
{ {
Expr **init_expressions = initializer->expr_initializer.initializer_expr; Expr **init_expressions = initializer->expr_initializer.initializer_expr;
bool is_structlike = type_is_structlike(assigned->canonical);
VECEACH(init_expressions, i) VECEACH(init_expressions, i)
{ {
Expr *expr = init_expressions[i]; Expr *expr = init_expressions[i];
// 1. Ensure that're seeing expr = expr on the top level. // 1. Ensure that're seeing expr = expr on the top level.
if (expr->expr_kind != EXPR_BINARY || expr->binary_expr.operator != BINARYOP_ASSIGN) if (expr->expr_kind != EXPR_BINARY || expr->binary_expr.operator != BINARYOP_ASSIGN)
{ {
SEMA_ERROR(expr, "Expected an initializer on the format 'foo = 123' here."); if (is_structlike)
{
SEMA_ERROR(expr, "Expected an initializer on the format 'foo = 123' here.");
}
else
{
SEMA_ERROR(expr, "Expected an initializer on the format '[1] = 123' here.");
}
return false; return false;
} }
Expr *init_expr = expr->binary_expr.left; Expr *init_expr = expr->binary_expr.left;
DesignatedPath path = { .type = assigned }; DesignatedPath path = { .type = assigned };
DesignatedPath *last_path = sema_analyse_init_path(context, &path, init_expr); bool has_reported_error = false;
if (!last_path) bool has_found_match = false;
DesignatedPath *last_path = sema_analyse_init_path(context, &path, init_expr, &has_found_match, &has_reported_error);
if (!has_reported_error && !last_path)
{ {
SEMA_ERROR(expr, "This is not a valid member of '%s'.", type_to_error_string(assigned)); SEMA_ERROR(expr, "This is not a valid member of '%s'.", type_to_error_string(assigned));
return false; return false;
@@ -773,6 +776,10 @@ static bool sema_expr_analyse_struct_designated_initializer(Context *context, Ty
return true; return true;
} }
/**
* Perform analysis for a plain initializer, that is one initializing all fields.
* @return true if analysis succeeds.
*/
static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, Decl *assigned, Expr *initializer) static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, Decl *assigned, Expr *initializer)
{ {
Expr **elements = initializer->expr_initializer.initializer_expr; Expr **elements = initializer->expr_initializer.initializer_expr;
@@ -781,17 +788,74 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context,
unsigned size = vec_size(elements); unsigned size = vec_size(elements);
unsigned expected_members = vec_size(members); unsigned expected_members = vec_size(members);
// For struct number of members must be the same as the size of the struct. // 1. For struct number of members must be the same as the size of the struct.
// Since we already handled the case with an empty initializer before going here
assert(size > 0); // zero entries must be an error.
assert(size > 0 && "We should already have handled the size == 0 case.");
if (expected_members == 0) if (expected_members == 0)
{ {
// Generate a nice error message for zero.
SEMA_ERROR(elements[0], "Too many elements in initializer, it must be empty.");
return false;
}
// 2. In case of a union, only expect a single entry.
if (assigned->decl_kind == DECL_UNION) expected_members = 1;
// 3. Loop through all elements.
VECEACH(elements, i)
{
// 4. Check if we exceeded the list of elements in the struct/union.
// This way we can check the other elements which might help the
// user pinpoint where they put the double elements.
if (i >= expected_members)
{
SEMA_ERROR(elements[i], "Too many elements in initializer, expected only %d.", expected_members);
return false;
}
// 5. We know the required type, so resolve the expression.
if (!sema_analyse_expr_of_required_type(context, members[i]->type, elements[i])) return false;
}
// 6. There's the case of too few values as well. Mark the last element as wrong.
if (expected_members > size)
{
SEMA_ERROR(elements[size - 1], "Too few elements in initializer, there should be elements after this one.");
return false;
}
// 7. Done!
return true;
}
/**
* Perform analysis for a plain initializer, that is one initializing all fields.
* @return true if analysis succeeds.
*/
static inline bool sema_expr_analyse_array_plain_initializer(Context *context, Type *assigned, Expr *initializer)
{
Expr **elements = initializer->expr_initializer.initializer_expr;
assert(assigned->type_kind == TYPE_ARRAY && "The other types are not done yet.");
Type *inner_type = type_get_indexed_type(assigned);
assert(inner_type);
initializer->expr_initializer.init_type = INITIALIZER_NORMAL;
unsigned size = vec_size(elements);
unsigned expected_members = assigned->array.len;
assert(size > 0 && "We should already have handled the size == 0 case.");
if (expected_members == 0)
{
// Generate a nice error message for zero.
SEMA_ERROR(elements[0], "Too many elements in initializer, it must be empty."); SEMA_ERROR(elements[0], "Too many elements in initializer, it must be empty.");
return false; return false;
} }
bool is_union = assigned->decl_kind == DECL_UNION;
expected_members = is_union ? 1 : expected_members;
VECEACH(elements, i) VECEACH(elements, i)
{ {
if (i >= expected_members) if (i >= expected_members)
@@ -799,17 +863,20 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context,
SEMA_ERROR(elements[i], "Too many elements in initializer, expected only %d.", expected_members); SEMA_ERROR(elements[i], "Too many elements in initializer, expected only %d.", expected_members);
return false; return false;
} }
if (!sema_analyse_expr_of_required_type(context, members[i]->type, elements[i])) return false; if (!sema_analyse_expr_of_required_type(context, inner_type, elements[i])) return false;
} }
if (expected_members > size) if (expected_members > size)
{ {
SEMA_ERROR(elements[size - 1], "Few elements in initializer, there should be elements after this one."); SEMA_ERROR(elements[size - 1], "Too few elements in initializer, %d elements are needed.", expected_members);
return false; return false;
} }
// 7. Done!
return true; return true;
} }
static inline bool sema_expr_analyse_struct_initializer(Context *context, Type *assigned, Expr *expr) static inline bool sema_expr_analyse_initializer(Context *context, Type *assigned, Expr *expr)
{ {
expr->type = assigned; expr->type = assigned;
@@ -826,11 +893,18 @@ static inline bool sema_expr_analyse_struct_initializer(Context *context, Type *
// this means that in this case we're actually not resolving macros here. // this means that in this case we're actually not resolving macros here.
if (init_expressions[0]->expr_kind == EXPR_BINARY && init_expressions[0]->binary_expr.operator == BINARYOP_ASSIGN) if (init_expressions[0]->expr_kind == EXPR_BINARY && init_expressions[0]->binary_expr.operator == BINARYOP_ASSIGN)
{ {
return sema_expr_analyse_struct_designated_initializer(context, assigned, expr); return sema_expr_analyse_designated_initializer(context, assigned, expr);
} }
// 3. Otherwise use the plain initializer. // 3. Otherwise use the plain initializer.
return sema_expr_analyse_struct_plain_initializer(context, assigned->decl, expr); if (assigned->type_kind == TYPE_ARRAY)
{
return sema_expr_analyse_array_plain_initializer(context, assigned, expr);
}
else
{
return sema_expr_analyse_struct_plain_initializer(context, assigned->decl, expr);
}
} }
static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to, Expr *expr) static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to, Expr *expr)
@@ -842,9 +916,8 @@ static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to
{ {
case TYPE_STRUCT: case TYPE_STRUCT:
case TYPE_UNION: case TYPE_UNION:
return sema_expr_analyse_struct_initializer(context, assigned, expr);
case TYPE_ARRAY: case TYPE_ARRAY:
TODO return sema_expr_analyse_initializer(context, assigned, expr);
case TYPE_VARARRAY: case TYPE_VARARRAY:
TODO TODO
default: default: