diff --git a/core/_preload.odin b/core/_preload.odin index bc6e1174b..c540a233f 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -34,6 +34,8 @@ CallingConvention :: enum { } // IMPORTANT NOTE(bill): Do not change the order of any of this data // The compiler relies upon this _exact_ order + + TypeInfoEnumValue :: raw_union { f: f64; i: i128; @@ -49,6 +51,7 @@ TypeInfoRecord :: struct #ordered { custom_align: bool; } + TypeInfo :: union { size: int; align: int; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f807d3402..ebddbb0f3 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -692,14 +692,16 @@ void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map // Returns filled field_count -isize check_fields(Checker *c, AstNode *node, Array decls, - Entity **fields, isize field_count, - String context) { +Array check_fields(Checker *c, AstNode *node, Array decls, + isize init_field_capacity, String context) { gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); defer (gb_temp_arena_memory_end(tmp)); + Array fields = {}; + array_init(&fields, heap_allocator(), init_field_capacity); + Map entity_map = {}; - map_init_with_reserve(&entity_map, c->tmp_allocator, 2*field_count); + map_init_with_reserve(&entity_map, c->tmp_allocator, 2*init_field_capacity); Entity *using_index_expr = nullptr; @@ -707,14 +709,33 @@ isize check_fields(Checker *c, AstNode *node, Array decls, GB_ASSERT(node->kind != AstNode_UnionType); } - isize field_index = 0; + check_collect_entities(c, decls, false); + for_array(i, c->context.scope->elements.entries) { + Entity *e = c->context.scope->elements.entries[i].value; + if (e->token.string == "EnumValue") { + // gb_printf_err("EnumValue\n"); + } + DeclInfo *d = nullptr; + switch (e->kind) { + default: continue; + case Entity_Constant: + case Entity_TypeName: + d = decl_info_of_entity(&c->info, e); + if (d != nullptr) { + check_entity_decl(c, e, d, nullptr); + } + break; + } + } + for_array(decl_index, decls) { AstNode *decl = decls[decl_index]; - if (decl->kind != AstNode_ValueDecl) { - continue; - } + if (decl->kind != AstNode_ValueDecl) continue; + ast_node(vd, ValueDecl, decl); + if (!vd->is_mutable) continue; + Type *type = nullptr; if (vd->type != nullptr) { type = check_type(c, vd->type); @@ -746,10 +767,10 @@ isize check_fields(Checker *c, AstNode *node, Array decls, Token name_token = name->Ident.token; - Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, is_using, cast(i32)field_index); + Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, is_using, cast(i32)fields.count); e->identifier = name; if (name_token.string == "_") { - fields[field_index++] = e; + array_add(&fields, e); } else if (name_token.string == "__tag") { error(name, "`__tag` is a reserved identifier for fields"); } else { @@ -763,7 +784,7 @@ isize check_fields(Checker *c, AstNode *node, Array decls, error(e->token, "\tpreviously declared"); } else { map_set(&entity_map, key, e); - fields[field_index++] = e; + array_add(&fields, e); add_entity(c, c->context.scope, name, e); } add_entity_use(c, name, e); @@ -792,9 +813,9 @@ isize check_fields(Checker *c, AstNode *node, Array decls, } } if (ok) { - using_index_expr = fields[field_index-1]; + using_index_expr = fields[fields.count-1]; } else { - fields[field_index-1]->flags &= ~EntityFlag_Using; + fields[fields.count-1]->flags &= ~EntityFlag_Using; error(name_token, "Previous `using` for an index expression `%.*s`", LIT(name_token.string)); } } else { @@ -810,7 +831,7 @@ isize check_fields(Checker *c, AstNode *node, Array decls, } - return field_index; + return fields; } @@ -863,26 +884,26 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { GB_ASSERT(is_type_struct(struct_type)); ast_node(st, StructType, node); - isize field_count = 0; + isize min_field_count = 0; for_array(field_index, st->fields) { AstNode *field = st->fields[field_index]; switch (field->kind) { case_ast_node(f, ValueDecl, field); - field_count += f->names.count; + min_field_count += f->names.count; case_end; } + } + struct_type->Record.names = make_names_field_for_record(c, c->context.scope); - Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); - - field_count = check_fields(c, node, st->fields, fields, field_count, str_lit("struct")); + auto fields = check_fields(c, node, st->fields, min_field_count, str_lit("struct")); + struct_type->Record.scope = c->context.scope; struct_type->Record.is_packed = st->is_packed; struct_type->Record.is_ordered = st->is_ordered; - struct_type->Record.fields = fields; - struct_type->Record.fields_in_src_order = fields; - struct_type->Record.field_count = field_count; - struct_type->Record.names = make_names_field_for_record(c, c->context.scope); + struct_type->Record.fields = fields.data; + struct_type->Record.fields_in_src_order = fields.data; + struct_type->Record.field_count = fields.count; type_set_offsets(c->allocator, struct_type); @@ -893,8 +914,8 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { struct_type->Record.offsets = nullptr; // NOTE(bill): Reorder fields for reduced size/performance - Entity **reordered_fields = gb_alloc_array(c->allocator, Entity *, field_count); - for (isize i = 0; i < field_count; i++) { + Entity **reordered_fields = gb_alloc_array(c->allocator, Entity *, fields.count); + for (isize i = 0; i < fields.count; i++) { reordered_fields[i] = struct_type->Record.fields_in_src_order[i]; } @@ -902,9 +923,9 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { // TODO(bill): Probably make an inline sorting procedure rather than use global variables __checker_allocator = c->allocator; // NOTE(bill): compound literal order must match source not layout - gb_sort_array(reordered_fields, field_count, cmp_reorder_struct_fields); + gb_sort_array(reordered_fields, fields.count, cmp_reorder_struct_fields); - for (isize i = 0; i < field_count; i++) { + for (isize i = 0; i < fields.count; i++) { reordered_fields[i]->Variable.field_index = i; } @@ -959,12 +980,12 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n ast_node(ut, UnionType, node); isize variant_count = ut->variants.count+1; - isize field_count = 0; + isize min_field_count = 0; for_array(i, ut->fields) { AstNode *field = ut->fields[i]; switch (field->kind) { case_ast_node(f, ValueDecl, field); - field_count += f->names.count; + min_field_count += f->names.count; case_end; } } @@ -977,23 +998,23 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n Entity *using_index_expr = nullptr; - Entity **variants = gb_alloc_array(c->allocator, Entity *, variant_count); - Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); + Array variants = {}; + array_init(&variants, heap_allocator(), variant_count); - isize variant_index = 0; - variants[variant_index++] = make_entity_type_name(c->allocator, c->context.scope, empty_token, nullptr); + array_add(&variants, make_entity_type_name(c->allocator, c->context.scope, empty_token, nullptr)); - field_count = check_fields(c, nullptr, ut->fields, fields, field_count, str_lit("union")); + auto fields = check_fields(c, nullptr, ut->fields, min_field_count, str_lit("union")); - for (isize i = 0; i < field_count; i++) { + for (isize i = 0; i < fields.count; i++) { Entity *f = fields[i]; String name = f->token.string; map_set(&entity_map, hash_string(name), f); } - union_type->Record.fields = fields; - union_type->Record.fields_in_src_order = fields; - union_type->Record.field_count = field_count; + union_type->Record.scope = c->context.scope; + union_type->Record.fields = fields.data; + union_type->Record.fields_in_src_order = fields.data; + union_type->Record.field_count = fields.count; union_type->Record.are_offsets_set = false; union_type->Record.is_ordered = true; { @@ -1035,17 +1056,16 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n AstNode *dummy_struct = ast_struct_type(c->curr_ast_file, token, list, list_count, false, true, nullptr); check_open_scope(c, dummy_struct); - Entity **fields = gb_alloc_array(c->allocator, Entity *, list_count); - isize field_count = check_fields(c, dummy_struct, list, fields, list_count, str_lit("variant")); + base_type->Record.names = make_names_field_for_record(c, c->context.scope); + auto fields = check_fields(c, dummy_struct, list, list_count, str_lit("variant")); base_type->Record.is_packed = false; base_type->Record.is_ordered = true; - base_type->Record.fields = fields; - base_type->Record.fields_in_src_order = fields; - base_type->Record.field_count = field_count; - base_type->Record.names = make_names_field_for_record(c, c->context.scope); - base_type->Record.node = dummy_struct; - base_type->Record.variant_parent = named_type != nullptr ? named_type : union_type; - base_type->Record.variant_index = variant_index; + base_type->Record.fields = fields.data; + base_type->Record.fields_in_src_order = fields.data; + base_type->Record.field_count = fields.count; + base_type->Record.node = dummy_struct; + base_type->Record.variant_parent = named_type != nullptr ? named_type : union_type; + base_type->Record.variant_index = variants.count; type_set_offsets(c->allocator, base_type); @@ -1069,7 +1089,7 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string)); } else { map_set(&entity_map, key, e); - variants[variant_index++] = e; + array_add(&variants, e); } add_entity_use(c, f->name, e); } @@ -1077,8 +1097,8 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n type_set_offsets(c->allocator, union_type); - union_type->Record.variants = variants; - union_type->Record.variant_count = variant_index; + union_type->Record.variants = variants.data; + union_type->Record.variant_count = variants.count; } void check_raw_union_type(Checker *c, Type *union_type, AstNode *node) { @@ -1086,23 +1106,23 @@ void check_raw_union_type(Checker *c, Type *union_type, AstNode *node) { GB_ASSERT(is_type_raw_union(union_type)); ast_node(ut, RawUnionType, node); - isize field_count = 0; + isize min_field_count = 0; for_array(i, ut->fields) { AstNode *field = ut->fields[i]; switch (field->kind) { case_ast_node(f, ValueDecl, field); - field_count += f->names.count; + min_field_count += f->names.count; case_end; } } - Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); - - field_count = check_fields(c, node, ut->fields, fields, field_count, str_lit("raw_union")); - - union_type->Record.fields = fields; - union_type->Record.field_count = field_count; union_type->Record.names = make_names_field_for_record(c, c->context.scope); + + auto fields = check_fields(c, node, ut->fields, min_field_count, str_lit("raw_union")); + + union_type->Record.scope = c->context.scope; + union_type->Record.fields = fields.data; + union_type->Record.field_count = fields.count; } @@ -1133,8 +1153,8 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod Map entity_map = {}; // Key: String map_init_with_reserve(&entity_map, c->tmp_allocator, 2*(et->fields.count)); - Entity **fields = gb_alloc_array(c->allocator, Entity *, et->fields.count); - isize field_count = 0; + Array fields = {}; + array_init(&fields, c->allocator, et->fields.count); Type *constant_type = enum_type; if (named_type != nullptr) { @@ -1222,18 +1242,18 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod } else { map_set(&entity_map, key, e); add_entity(c, c->context.scope, nullptr, e); - fields[field_count++] = e; + array_add(&fields, e); add_entity_use(c, field, e); } } - GB_ASSERT(field_count <= et->fields.count); + GB_ASSERT(fields.count <= et->fields.count); - enum_type->Record.fields = fields; - enum_type->Record.field_count = field_count; + enum_type->Record.fields = fields.data; + enum_type->Record.field_count = fields.count; enum_type->Record.enum_count = make_entity_constant(c->allocator, c->context.scope, - make_token_ident(str_lit("count")), t_int, exact_value_i64(field_count)); + make_token_ident(str_lit("count")), t_int, exact_value_i64(fields.count)); enum_type->Record.enum_min_value = make_entity_constant(c->allocator, c->context.scope, make_token_ident(str_lit("min_value")), constant_type, min_value); enum_type->Record.enum_max_value = make_entity_constant(c->allocator, c->context.scope, diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index fe70fd672..c7aa5424f 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -473,7 +473,39 @@ bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bo switch (e->kind) { case Entity_TypeName: { Type *t = base_type(e->type); - if (is_type_union(t)) { + if (t->kind == Type_Record) { + Scope *s = t->Record.scope; + if (s != nullptr) { + for_array(i, s->elements.entries) { + Entity *f = s->elements.entries[i].value; + if (f->kind != Entity_Variable) { + Entity *found = scope_insert_entity(c->context.scope, f); + if (found != nullptr) { + gbString expr_str = expr_to_string(expr); + error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string)); + gb_string_free(expr_str); + return false; + } + f->using_parent = e; + } + } + } else if (is_type_enum(t)) { + for (isize i = 0; i < t->Record.field_count; i++) { + Entity *f = t->Record.fields[i]; + Entity *found = scope_insert_entity(c->context.scope, f); + if (found != nullptr) { + gbString expr_str = expr_to_string(expr); + error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string)); + gb_string_free(expr_str); + return false; + } + f->using_parent = e; + } + } + } else { + error(us->token, "`using` can be only applied to record type entities"); + } + /* if (is_type_union(t)) { TokenPos pos = ast_node_token(expr).pos; for (isize i = 1; i < t->Record.variant_count; i++) { Entity *f = t->Record.variants[i]; @@ -502,7 +534,7 @@ bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bo } else { error(us->token, "`using` can be only applied to `union` or `enum` type entities"); - } + } */ } break; case Entity_ImportName: { diff --git a/src/checker.cpp b/src/checker.cpp index 178aff62a..faeff2212 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -233,6 +233,7 @@ struct Scope { bool is_global; bool is_file; bool is_init; + bool is_record; bool has_been_imported; // This is only applicable to file scopes AstFile * file; }; @@ -447,8 +448,16 @@ void check_open_scope(Checker *c, AstNode *node) { is_ast_node_type(node)); Scope *scope = make_scope(c->context.scope, c->allocator); add_scope(c, node, scope); - if (node->kind == AstNode_ProcType) { + switch (node->kind) { + case AstNode_ProcType: scope->is_proc = true; + break; + case AstNode_StructType: + case AstNode_EnumType: + case AstNode_UnionType: + case AstNode_RawUnionType: + scope->is_record = true; + break; } c->context.scope = scope; c->context.stmt_state_flags |= StmtStateFlag_bounds_check; @@ -1307,9 +1316,26 @@ void init_preload(Checker *c) { GB_ASSERT(is_type_union(type_info_entity->type)); TypeRecord *record = &base_type(type_info_entity->type)->Record; - t_type_info_record = find_core_entity(c, str_lit("TypeInfoRecord"))->type; + // Entity *type_info_record = current_scope_lookup_entity(record->scope, str_lit("Record")); + // if (type_info_record == nullptr) { + // compiler_error("Could not find type declaration for TypeInfo.Record\n" + // "Is `_preload.odin` missing from the `core` directory relative to the odin executable?"); + // } + // Entity *type_info_enum_value = current_scope_lookup_entity(record->scope, str_lit("EnumValue")); + // if (type_info_record == nullptr) { + // compiler_error("Could not find type declaration for TypeInfo.EnumValue\n" + // "Is `_preload.odin` missing from the `core` directory relative to the odin executable?"); + // } + + // GB_ASSERT(type_info_record->type != nullptr); + // GB_ASSERT(type_info_enum_value->type != nullptr); + Entity *type_info_record = find_core_entity(c, str_lit("TypeInfoRecord")); + Entity *type_info_enum_value = find_core_entity(c, str_lit("TypeInfoEnumValue")); + + + t_type_info_record = type_info_record->type; t_type_info_record_ptr = make_type_pointer(c->allocator, t_type_info_record); - t_type_info_enum_value = find_core_entity(c, str_lit("TypeInfoEnumValue"))->type; + t_type_info_enum_value = type_info_enum_value->type; t_type_info_enum_value_ptr = make_type_pointer(c->allocator, t_type_info_enum_value); @@ -1692,20 +1718,29 @@ void check_collect_entities(Checker *c, Array nodes, bool is_file_sco continue; } + Token token = name->Ident.token; + if (token.string == "EnumValue") { + gb_printf_err("EnumValue %p\n", name); + } + AstNode *fl = c->context.curr_foreign_library; DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, c->context.decl); Entity *e = nullptr; if (is_ast_node_type(init)) { - e = make_entity_type_name(c->allocator, d->scope, name->Ident.token, nullptr); + e = make_entity_type_name(c->allocator, d->scope, token, nullptr); if (vd->type != nullptr) { error(name, "A type declaration cannot have an type parameter"); } d->type_expr = init; d->init_expr = init; } else if (init->kind == AstNode_ProcLit) { + if (c->context.scope->is_record) { + error(name, "Procedure declarations are not allowed within a record"); + continue; + } ast_node(pl, ProcLit, init); - e = make_entity_procedure(c->allocator, d->scope, name->Ident.token, nullptr, pl->tags); + e = make_entity_procedure(c->allocator, d->scope, token, nullptr, pl->tags); if (fl != nullptr) { GB_ASSERT(fl->kind == AstNode_Ident); e->Procedure.foreign_library_ident = fl; @@ -1714,7 +1749,7 @@ void check_collect_entities(Checker *c, Array nodes, bool is_file_sco d->proc_lit = init; d->type_expr = pl->type; } else { - e = make_entity_constant(c->allocator, d->scope, name->Ident.token, nullptr, empty_exact_value); + e = make_entity_constant(c->allocator, d->scope, token, nullptr, empty_exact_value); d->type_expr = vd->type; d->init_expr = init; } @@ -1730,6 +1765,7 @@ void check_collect_entities(Checker *c, Array nodes, bool is_file_sco } + add_entity_and_decl_info(c, name, e, d); } diff --git a/src/ir.cpp b/src/ir.cpp index 9335a71fd..477ae799e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3632,10 +3632,13 @@ void ir_pop_target_list(irProcedure *proc) { void ir_gen_global_type_name(irModule *m, Entity *e, String name) { + if (e->type == nullptr) return; + irValue *t = ir_value_type_name(m->allocator, name, e->type); ir_module_add_value(m, e, t); map_set(&m->members, hash_string(name), t); + #if 0 if (is_type_union(e->type)) { Type *bt = base_type(e->type); // NOTE(bill): Zeroth entry is null (for `match type` stmts) @@ -3643,6 +3646,20 @@ void ir_gen_global_type_name(irModule *m, Entity *e, String name) { ir_mangle_add_sub_type_name(m, bt->Record.variants[j], name); } } + #endif + + Type *bt = base_type(e->type); + if (bt->kind == Type_Record) { + Scope *s = bt->Record.scope; + if (s != nullptr) { + for_array(i, s->elements.entries) { + Entity *e = s->elements.entries[i].value; + if (e->kind == Entity_TypeName) { + ir_mangle_add_sub_type_name(m, e, name); + } + } + } + } } diff --git a/src/parser.cpp b/src/parser.cpp index 2f3bdcab2..cb2026d49 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1366,6 +1366,7 @@ AstNode *ast_field_list(AstFile *f, Token token, Array list) { result->FieldList.list = list; return result; } + AstNode *ast_union_field(AstFile *f, AstNode *name, AstNode *list) { AstNode *result = make_ast_node(f, AstNode_UnionField); result->UnionField.name = name; @@ -3390,20 +3391,28 @@ AstNode *parse_record_field_list(AstFile *f, isize *name_count_) { while (f->curr_token.kind != Token_CloseBrace && f->curr_token.kind != Token_EOF) { AstNode *decl = parse_stmt(f); - if (decl->kind != AstNode_ValueDecl) { - error(decl, "Expected a field list, got %.*s", LIT(ast_node_strings[decl->kind])); - } else { - ast_node(vd, ValueDecl, decl); - if (vd->is_mutable) { - if (vd->flags&VarDeclFlag_thread_local) { - vd->flags &= ~VarDeclFlag_thread_local; - error(decl, "Field values cannot be #thread_local"); - } - array_add(&decls, decl); - total_name_count += vd->names.count; - } else { - error(decl, "Only variable declarations are allowed at the moment"); + switch (decl->kind) { + case AstNode_EmptyStmt: + case AstNode_BadStmt: + case AstNode_BadDecl: + break; + + case_ast_node(vd, ValueDecl, decl); + if (vd->flags&VarDeclFlag_thread_local) { + vd->flags &= ~VarDeclFlag_thread_local; + error(decl, "Field values cannot be #thread_local"); } + array_add(&decls, decl); + total_name_count += vd->names.count; + case_end; + + case AstNode_WhenStmt: + array_add(&decls, decl); + break; + + default: + error(decl, "Expected a value declaration, got %.*s", LIT(ast_node_strings[decl->kind])); + break; } } @@ -3683,8 +3692,8 @@ AstNode *parse_type_or_ident(AstFile *f) { Token open = expect_token_after(f, Token_OpenBrace, "struct"); isize name_count = 0; - AstNode *fields = parse_record_field_list(f, &name_count); - Token close = expect_token(f, Token_CloseBrace); + AstNode *fields = parse_record_field_list(f, &name_count); + Token close = expect_token(f, Token_CloseBrace); Array decls = {}; if (fields != nullptr) { @@ -3742,11 +3751,9 @@ AstNode *parse_type_or_ident(AstFile *f) { vd->flags &= ~VarDeclFlag_thread_local; error(decl, "Field values cannot be #thread_local"); } - array_add(&decls, decl); - total_decl_name_count += vd->names.count; - } else { - error(decl, "Only variable declarations are allowed at the moment"); } + array_add(&decls, decl); + total_decl_name_count += vd->names.count; } } } diff --git a/src/types.cpp b/src/types.cpp index 1516cd993..d8e87ff71 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -90,6 +90,7 @@ struct TypeRecord { i32 field_count; // == struct_offsets count Entity **fields_in_src_order; // Entity_Variable AstNode *node; + Scope * scope; // Entity_TypeName - union Entity **variants; @@ -1477,6 +1478,8 @@ Selection lookup_field_from_index(gbAllocator a, Type *type, i64 index) { gb_global Entity *entity__any_data = nullptr; gb_global Entity *entity__any_type_info = nullptr; +Entity *current_scope_lookup_entity(Scope *s, String name); + Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel) { GB_ASSERT(type_ != nullptr); @@ -1593,6 +1596,18 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n } } } + + if (type->kind == Type_Record) { + Scope *s = type->Record.scope; + if (s != nullptr) { + Entity *found = current_scope_lookup_entity(s, field_name); + if (found != nullptr && found->kind != Entity_Variable) { + sel.entity = found; + return sel; + } + } + } + } else if (type->kind == Type_Record) { for (isize i = 0; i < type->Record.field_count; i++) { Entity *f = type->Record.fields[i];