mirror of
https://github.com/ziglang/zig.git
synced 2025-02-04 20:02:47 +00:00
parsing code for defer and more
* disable goto and label support see #44 * refactor the way block contexts work
This commit is contained in:
parent
4339d55562
commit
6a2ede5a6e
@ -51,7 +51,7 @@ Expression = BlockExpression | NonBlockExpression
|
||||
|
||||
TypeExpr = PrefixOpExpression
|
||||
|
||||
NonBlockExpression = ReturnExpression | AssignmentExpression
|
||||
NonBlockExpression = ReturnExpression | AssignmentExpression | DeferExpression
|
||||
|
||||
AsmExpression = "asm" option("volatile") "(" "String" option(AsmOutput) ")"
|
||||
|
||||
@ -91,6 +91,8 @@ BoolOrExpression = BoolAndExpression "||" BoolOrExpression | BoolAndExpression
|
||||
|
||||
ReturnExpression = option("%" | "?") "return" option(Expression)
|
||||
|
||||
DeferExpression = option("%" | "?") "defer" option(Expression)
|
||||
|
||||
IfExpression = IfVarExpression | IfBoolExpression
|
||||
|
||||
IfBoolExpression = "if" "(" Expression ")" Expression option(Else)
|
||||
|
@ -24,7 +24,6 @@ struct TypeTableEntry;
|
||||
struct VariableTableEntry;
|
||||
struct ErrorTableEntry;
|
||||
struct BuiltinFnEntry;
|
||||
struct LabelTableEntry;
|
||||
struct TypeStructField;
|
||||
struct CodeGen;
|
||||
struct ConstExprValue;
|
||||
@ -118,6 +117,7 @@ enum NodeType {
|
||||
NodeTypeBlock,
|
||||
NodeTypeDirective,
|
||||
NodeTypeReturnExpr,
|
||||
NodeTypeDeferExpr,
|
||||
NodeTypeVariableDeclaration,
|
||||
NodeTypeTypeDecl,
|
||||
NodeTypeErrorValueDecl,
|
||||
@ -235,6 +235,16 @@ struct AstNodeReturnExpr {
|
||||
Expr resolved_expr;
|
||||
};
|
||||
|
||||
struct AstNodeDeferExpr {
|
||||
ReturnKind kind;
|
||||
AstNode *expr;
|
||||
|
||||
// populated by semantic analyzer:
|
||||
Expr resolved_expr;
|
||||
int index_in_block;
|
||||
LLVMBasicBlockRef basic_block;
|
||||
};
|
||||
|
||||
struct AstNodeVariableDeclaration {
|
||||
Buf symbol;
|
||||
bool is_const;
|
||||
@ -477,7 +487,6 @@ struct AstNodeWhileExpr {
|
||||
bool contains_break;
|
||||
bool contains_continue;
|
||||
Expr resolved_expr;
|
||||
BlockContext *block_context;
|
||||
};
|
||||
|
||||
struct AstNodeForExpr {
|
||||
@ -522,7 +531,6 @@ struct AstNodeLabel {
|
||||
Buf name;
|
||||
|
||||
// populated by semantic analyzer
|
||||
LabelTableEntry *label_entry;
|
||||
Expr resolved_expr;
|
||||
};
|
||||
|
||||
@ -530,7 +538,6 @@ struct AstNodeGoto {
|
||||
Buf name;
|
||||
|
||||
// populated by semantic analyzer
|
||||
LabelTableEntry *label_entry;
|
||||
Expr resolved_expr;
|
||||
};
|
||||
|
||||
@ -732,6 +739,7 @@ struct AstNode {
|
||||
AstNodeParamDecl param_decl;
|
||||
AstNodeBlock block;
|
||||
AstNodeReturnExpr return_expr;
|
||||
AstNodeDeferExpr defer_expr;
|
||||
AstNodeVariableDeclaration variable_declaration;
|
||||
AstNodeTypeDecl type_decl;
|
||||
AstNodeErrorValueDecl error_value_decl;
|
||||
@ -967,13 +975,8 @@ struct ImportTableEntry {
|
||||
|
||||
// reminder: hash tables must be initialized before use
|
||||
HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
|
||||
};
|
||||
|
||||
struct LabelTableEntry {
|
||||
AstNode *label_node;
|
||||
LLVMBasicBlockRef basic_block;
|
||||
bool used;
|
||||
bool entered_from_fallthrough;
|
||||
HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
|
||||
HashMap<Buf *, ErrorTableEntry *, buf_hash, buf_eql_buf> error_table;
|
||||
};
|
||||
|
||||
struct FnTableEntry {
|
||||
@ -992,8 +995,9 @@ struct FnTableEntry {
|
||||
bool is_test;
|
||||
uint32_t ref_count; // if this is 0 we don't have to codegen it
|
||||
|
||||
// reminder: hash tables must be initialized before use
|
||||
HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
|
||||
ZigList<AstNode *> cast_alloca_list;
|
||||
ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;
|
||||
ZigList<VariableTableEntry *> variable_list;
|
||||
};
|
||||
|
||||
enum BuiltinFnId {
|
||||
@ -1140,6 +1144,7 @@ struct VariableTableEntry {
|
||||
LLVMZigDILocalVariable *di_loc_var;
|
||||
int src_arg_index;
|
||||
int gen_arg_index;
|
||||
BlockContext *block_context;
|
||||
};
|
||||
|
||||
struct ErrorTableEntry {
|
||||
@ -1148,19 +1153,32 @@ struct ErrorTableEntry {
|
||||
AstNode *decl_node;
|
||||
};
|
||||
|
||||
enum BlockExitPath {
|
||||
BlockExitPathFallthrough,
|
||||
BlockExitPathReturn,
|
||||
BlockExitPathGoto,
|
||||
};
|
||||
|
||||
struct BlockContext {
|
||||
AstNode *node; // either NodeTypeFnDef or NodeTypeBlock or NodeTypeRoot
|
||||
FnTableEntry *fn_entry; // null at the module scope
|
||||
BlockContext *parent; // null when this is the root
|
||||
// One of: NodeTypeFnDef, NodeTypeBlock, NodeTypeRoot, NodeTypeDeferExpr, NodeTypeVariableDeclaration
|
||||
AstNode *node;
|
||||
|
||||
// any variables that are introduced by this scope
|
||||
HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
|
||||
HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
|
||||
HashMap<Buf *, ErrorTableEntry *, buf_hash, buf_eql_buf> error_table;
|
||||
ZigList<AstNode *> cast_alloca_list;
|
||||
ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;
|
||||
ZigList<VariableTableEntry *> variable_list;
|
||||
|
||||
// if the block is inside a function, this is the function it is in:
|
||||
FnTableEntry *fn_entry;
|
||||
|
||||
// if the block has a parent, this is it
|
||||
BlockContext *parent;
|
||||
|
||||
// if break or continue is valid in this context, this is the loop node that
|
||||
// it would pertain to
|
||||
AstNode *parent_loop_node;
|
||||
|
||||
LLVMZigDIScope *di_scope;
|
||||
Buf *c_import_buf;
|
||||
bool block_exit_paths[3]; // one for each BlockExitPath
|
||||
};
|
||||
|
||||
enum CIntType {
|
||||
|
362
src/analyze.cpp
362
src/analyze.cpp
@ -57,6 +57,7 @@ static AstNode *first_executing_node(AstNode *node) {
|
||||
case NodeTypeBlock:
|
||||
case NodeTypeDirective:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeDeferExpr:
|
||||
case NodeTypeVariableDeclaration:
|
||||
case NodeTypeTypeDecl:
|
||||
case NodeTypeErrorValueDecl:
|
||||
@ -865,23 +866,6 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
|
||||
}
|
||||
}
|
||||
|
||||
static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry) {
|
||||
assert(node->type == NodeTypeBlock);
|
||||
|
||||
for (int i = 0; i < node->data.block.statements.length; i += 1) {
|
||||
AstNode *label_node = node->data.block.statements.at(i);
|
||||
if (label_node->type != NodeTypeLabel)
|
||||
continue;
|
||||
|
||||
LabelTableEntry *label_entry = allocate<LabelTableEntry>(1);
|
||||
label_entry->label_node = label_node;
|
||||
Buf *name = &label_node->data.label.name;
|
||||
fn_table_entry->label_table.put(name, label_entry);
|
||||
|
||||
label_node->data.label.label_entry = label_entry;
|
||||
}
|
||||
}
|
||||
|
||||
static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *enum_type) {
|
||||
// if you change this logic you likely must also change similar logic in parseh.cpp
|
||||
assert(enum_type->id == TypeTableEntryIdEnum);
|
||||
@ -1233,7 +1217,6 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,
|
||||
fn_table_entry->fn_def_node = fn_def_node;
|
||||
fn_table_entry->internal_linkage = !is_c_compat;
|
||||
fn_table_entry->is_extern = is_extern;
|
||||
fn_table_entry->label_table.init(8);
|
||||
fn_table_entry->member_of_struct = struct_type;
|
||||
fn_table_entry->ref_count = (proto_node->data.fn_proto.visib_mod == VisibModExport) ? 1 : 0;
|
||||
|
||||
@ -1272,10 +1255,6 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,
|
||||
proto_node->data.fn_proto.fn_table_entry = fn_table_entry;
|
||||
resolve_function_proto(g, proto_node, fn_table_entry, import);
|
||||
|
||||
if (fn_def_node) {
|
||||
preview_function_labels(g, fn_def_node->data.fn_def.body, fn_table_entry);
|
||||
}
|
||||
|
||||
if (is_pub && !struct_type) {
|
||||
for (int i = 0; i < import->importers.length; i += 1) {
|
||||
ImporterInfo importer = import->importers.at(i);
|
||||
@ -1317,13 +1296,13 @@ static void resolve_error_value_decl(CodeGen *g, ImportTableEntry *import, AstNo
|
||||
|
||||
ErrorTableEntry *err = node->data.error_value_decl.err;
|
||||
|
||||
import->block_context->error_table.put(&err->name, err);
|
||||
import->error_table.put(&err->name, err);
|
||||
|
||||
bool is_pub = (node->data.error_value_decl.visib_mod != VisibModPrivate);
|
||||
if (is_pub) {
|
||||
for (int i = 0; i < import->importers.length; i += 1) {
|
||||
ImporterInfo importer = import->importers.at(i);
|
||||
importer.import->block_context->error_table.put(&err->name, err);
|
||||
importer.import->error_table.put(&err->name, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1347,6 +1326,8 @@ static void resolve_c_import_decl(CodeGen *g, ImportTableEntry *parent_import, A
|
||||
|
||||
ImportTableEntry *child_import = allocate<ImportTableEntry>(1);
|
||||
child_import->fn_table.init(32);
|
||||
child_import->type_table.init(8);
|
||||
child_import->error_table.init(8);
|
||||
child_import->c_import_node = node;
|
||||
|
||||
ZigList<ErrorMsg *> errors = {0};
|
||||
@ -1442,19 +1423,19 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
|
||||
}
|
||||
}
|
||||
|
||||
import->block_context->type_table.put(decl_name, entry);
|
||||
import->type_table.put(decl_name, entry);
|
||||
|
||||
bool is_pub = (node->data.type_decl.visib_mod != VisibModPrivate);
|
||||
if (is_pub) {
|
||||
for (int i = 0; i < import->importers.length; i += 1) {
|
||||
ImporterInfo importer = import->importers.at(i);
|
||||
auto table_entry = importer.import->block_context->type_table.maybe_get(&entry->name);
|
||||
auto table_entry = importer.import->type_table.maybe_get(&entry->name);
|
||||
if (table_entry) {
|
||||
add_node_error(g, importer.source_node,
|
||||
buf_sprintf("import of type '%s' overrides existing definition",
|
||||
buf_ptr(&entry->name)));
|
||||
} else {
|
||||
importer.import->block_context->type_table.put(&entry->name, entry);
|
||||
importer.import->type_table.put(&entry->name, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1475,6 +1456,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeDeferExpr:
|
||||
case NodeTypeRoot:
|
||||
case NodeTypeBlock:
|
||||
case NodeTypeBinOpExpr:
|
||||
@ -1953,9 +1935,7 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
|
||||
BlockContext *context = allocate<BlockContext>(1);
|
||||
context->node = node;
|
||||
context->parent = parent;
|
||||
context->variable_table.init(8);
|
||||
context->type_table.init(8);
|
||||
context->error_table.init(8);
|
||||
context->variable_table.init(4);
|
||||
|
||||
if (parent) {
|
||||
context->parent_loop_node = parent->parent_loop_node;
|
||||
@ -1976,10 +1956,10 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
|
||||
return context;
|
||||
}
|
||||
|
||||
static VariableTableEntry *find_local_variable(BlockContext *context, Buf *name) {
|
||||
while (context && context->fn_entry) {
|
||||
static VariableTableEntry *find_variable(BlockContext *context, Buf *name, bool local_only) {
|
||||
while (context && (!local_only || context->fn_entry)) {
|
||||
auto entry = context->variable_table.maybe_get(name);
|
||||
if (entry != nullptr)
|
||||
if (entry)
|
||||
return entry->value;
|
||||
|
||||
context = context->parent;
|
||||
@ -1987,26 +1967,12 @@ static VariableTableEntry *find_local_variable(BlockContext *context, Buf *name)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VariableTableEntry *find_variable(BlockContext *context, Buf *name) {
|
||||
while (context) {
|
||||
auto entry = context->variable_table.maybe_get(name);
|
||||
if (entry != nullptr)
|
||||
return entry->value;
|
||||
|
||||
context = context->parent;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TypeTableEntry *find_container(BlockContext *context, Buf *name) {
|
||||
while (context) {
|
||||
auto entry = context->type_table.maybe_get(name);
|
||||
if (entry != nullptr)
|
||||
return entry->value;
|
||||
|
||||
context = context->parent;
|
||||
}
|
||||
return nullptr;
|
||||
static TypeTableEntry *find_container(ImportTableEntry *import, Buf *name) {
|
||||
auto entry = import->type_table.maybe_get(name);
|
||||
if (entry)
|
||||
return entry->value;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static TypeEnumField *get_enum_field(TypeTableEntry *enum_type, Buf *name) {
|
||||
@ -2034,7 +2000,7 @@ static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *imp
|
||||
StructValExprCodeGen *codegen = &field_access_node->data.field_access_expr.resolved_struct_val_expr;
|
||||
codegen->type_entry = enum_type;
|
||||
codegen->source_node = field_access_node;
|
||||
context->struct_val_expr_alloca_list.append(codegen);
|
||||
context->fn_entry->struct_val_expr_alloca_list.append(codegen);
|
||||
|
||||
Expr *expr = get_resolved_expr(field_access_node);
|
||||
expr->const_val.ok = false;
|
||||
@ -2099,7 +2065,6 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry
|
||||
StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr;
|
||||
codegen->type_entry = container_type;
|
||||
codegen->source_node = node;
|
||||
context->struct_val_expr_alloca_list.append(codegen);
|
||||
|
||||
|
||||
int expr_field_count = container_init_expr->entries.length;
|
||||
@ -2150,6 +2115,9 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry
|
||||
const_val->ok = false;
|
||||
}
|
||||
}
|
||||
if (!const_val->ok) {
|
||||
context->fn_entry->struct_val_expr_alloca_list.append(codegen);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < actual_field_count; i += 1) {
|
||||
@ -2192,7 +2160,9 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry
|
||||
StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr;
|
||||
codegen->type_entry = fixed_size_array_type;
|
||||
codegen->source_node = node;
|
||||
context->struct_val_expr_alloca_list.append(codegen);
|
||||
if (!const_val->ok) {
|
||||
context->fn_entry->struct_val_expr_alloca_list.append(codegen);
|
||||
}
|
||||
|
||||
return fixed_size_array_type;
|
||||
} else if (container_type->id == TypeTableEntryIdArray) {
|
||||
@ -2339,7 +2309,7 @@ static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import,
|
||||
if (return_type->id != TypeTableEntryIdInvalid) {
|
||||
node->data.slice_expr.resolved_struct_val_expr.type_entry = return_type;
|
||||
node->data.slice_expr.resolved_struct_val_expr.source_node = node;
|
||||
context->struct_val_expr_alloca_list.append(&node->data.slice_expr.resolved_struct_val_expr);
|
||||
context->fn_entry->struct_val_expr_alloca_list.append(&node->data.slice_expr.resolved_struct_val_expr);
|
||||
}
|
||||
|
||||
analyze_expression(g, import, context, g->builtin_types.entry_isize, node->data.slice_expr.start);
|
||||
@ -2518,7 +2488,7 @@ static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode *
|
||||
static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import,
|
||||
BlockContext *context, AstNode *node, Buf *err_name)
|
||||
{
|
||||
auto err_table_entry = import->block_context->error_table.maybe_get(err_name);
|
||||
auto err_table_entry = import->error_table.maybe_get(err_name);
|
||||
|
||||
if (err_table_entry) {
|
||||
return resolve_expr_const_val_as_err(g, node, err_table_entry->value);
|
||||
@ -2545,7 +2515,7 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import,
|
||||
return resolve_expr_const_val_as_type(g, node, primitive_table_entry->value);
|
||||
}
|
||||
|
||||
VariableTableEntry *var = find_variable(context, variable_name);
|
||||
VariableTableEntry *var = find_variable(context, variable_name, false);
|
||||
if (var) {
|
||||
node->data.symbol_expr.variable = var;
|
||||
if (var->is_const) {
|
||||
@ -2561,7 +2531,7 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import,
|
||||
return var->type;
|
||||
}
|
||||
|
||||
TypeTableEntry *container_type = find_container(context, variable_name);
|
||||
TypeTableEntry *container_type = find_container(import, variable_name);
|
||||
if (container_type) {
|
||||
return resolve_expr_const_val_as_type(g, node, container_type);
|
||||
}
|
||||
@ -2641,7 +2611,7 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc
|
||||
if (purpose == LValPurposeAddressOf) {
|
||||
expected_rhs_type = analyze_symbol_expr(g, import, block_context, nullptr, lhs_node);
|
||||
} else {
|
||||
VariableTableEntry *var = find_variable(block_context, name);
|
||||
VariableTableEntry *var = find_variable(block_context, name, false);
|
||||
if (var) {
|
||||
if (var->is_const) {
|
||||
add_node_error(g, lhs_node, buf_sprintf("cannot assign to constant"));
|
||||
@ -3011,21 +2981,18 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
|
||||
}
|
||||
|
||||
// Set name to nullptr to make the variable anonymous (not visible to programmer).
|
||||
static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, BlockContext *context,
|
||||
Buf *name, TypeTableEntry *type_entry, bool is_const)
|
||||
static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, ImportTableEntry *import,
|
||||
BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const)
|
||||
{
|
||||
VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
|
||||
variable_entry->type = type_entry;
|
||||
variable_entry->block_context = context;
|
||||
|
||||
if (name) {
|
||||
buf_init_from_buf(&variable_entry->name, name);
|
||||
VariableTableEntry *existing_var;
|
||||
|
||||
if (context->fn_entry) {
|
||||
existing_var = find_local_variable(context, name);
|
||||
} else {
|
||||
existing_var = find_variable(context, name);
|
||||
}
|
||||
existing_var = find_variable(context, name, context->fn_entry != nullptr);
|
||||
|
||||
if (existing_var) {
|
||||
add_node_error(g, source_node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
|
||||
@ -3036,7 +3003,7 @@ static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, Block
|
||||
if (primitive_table_entry) {
|
||||
type = primitive_table_entry->value;
|
||||
} else {
|
||||
type = find_container(context, name);
|
||||
type = find_container(import, name);
|
||||
}
|
||||
if (type) {
|
||||
add_node_error(g, source_node, buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
|
||||
@ -3045,10 +3012,11 @@ static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, Block
|
||||
}
|
||||
|
||||
context->variable_table.put(&variable_entry->name, variable_entry);
|
||||
context->variable_list.append(variable_entry);
|
||||
} else {
|
||||
buf_init_from_str(&variable_entry->name, "_anon");
|
||||
context->variable_list.append(variable_entry);
|
||||
}
|
||||
if (context->fn_entry) {
|
||||
context->fn_entry->variable_list.append(variable_entry);
|
||||
}
|
||||
|
||||
variable_entry->is_const = is_const;
|
||||
@ -3075,7 +3043,7 @@ static TypeTableEntry *analyze_unwrap_error_expr(CodeGen *g, ImportTableEntry *i
|
||||
child_context = new_block_context(node, parent_context);
|
||||
var_node->block_context = child_context;
|
||||
Buf *var_name = &var_node->data.symbol_expr.symbol;
|
||||
node->data.unwrap_err_expr.var = add_local_var(g, var_node, child_context, var_name,
|
||||
node->data.unwrap_err_expr.var = add_local_var(g, var_node, import, child_context, var_name,
|
||||
g->builtin_types.entry_pure_error, true);
|
||||
} else {
|
||||
child_context = parent_context;
|
||||
@ -3153,7 +3121,7 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa
|
||||
TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type;
|
||||
assert(type != nullptr); // should have been caught by the parser
|
||||
|
||||
VariableTableEntry *var = add_local_var(g, source_node, context,
|
||||
VariableTableEntry *var = add_local_var(g, source_node, import, context,
|
||||
&variable_declaration->symbol, type, is_const);
|
||||
|
||||
variable_declaration->variable = var;
|
||||
@ -3199,7 +3167,7 @@ static TypeTableEntry *analyze_null_literal_expr(CodeGen *g, ImportTableEntry *i
|
||||
|
||||
node->data.null_literal.resolved_struct_val_expr.type_entry = expected_type;
|
||||
node->data.null_literal.resolved_struct_val_expr.source_node = node;
|
||||
block_context->struct_val_expr_alloca_list.append(&node->data.null_literal.resolved_struct_val_expr);
|
||||
block_context->fn_entry->struct_val_expr_alloca_list.append(&node->data.null_literal.resolved_struct_val_expr);
|
||||
|
||||
return resolve_expr_const_val_as_null(g, node, expected_type);
|
||||
}
|
||||
@ -3303,7 +3271,6 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import,
|
||||
|
||||
BlockContext *child_context = new_block_context(node, context);
|
||||
child_context->parent_loop_node = node;
|
||||
node->data.while_expr.block_context = child_context;
|
||||
|
||||
analyze_expression(g, import, child_context, g->builtin_types.entry_void, while_body_node);
|
||||
|
||||
@ -3359,16 +3326,16 @@ static TypeTableEntry *analyze_for_expr(CodeGen *g, ImportTableEntry *import, Bl
|
||||
AstNode *elem_var_node = node->data.for_expr.elem_node;
|
||||
elem_var_node->block_context = child_context;
|
||||
Buf *elem_var_name = &elem_var_node->data.symbol_expr.symbol;
|
||||
node->data.for_expr.elem_var = add_local_var(g, elem_var_node, child_context, elem_var_name, child_type, true);
|
||||
node->data.for_expr.elem_var = add_local_var(g, elem_var_node, import, child_context, elem_var_name, child_type, true);
|
||||
|
||||
AstNode *index_var_node = node->data.for_expr.index_node;
|
||||
if (index_var_node) {
|
||||
Buf *index_var_name = &index_var_node->data.symbol_expr.symbol;
|
||||
index_var_node->block_context = child_context;
|
||||
node->data.for_expr.index_var = add_local_var(g, index_var_node, child_context, index_var_name,
|
||||
node->data.for_expr.index_var = add_local_var(g, index_var_node, import, child_context, index_var_name,
|
||||
g->builtin_types.entry_isize, true);
|
||||
} else {
|
||||
node->data.for_expr.index_var = add_local_var(g, node, child_context, nullptr,
|
||||
node->data.for_expr.index_var = add_local_var(g, node, import, child_context, nullptr,
|
||||
g->builtin_types.entry_isize, true);
|
||||
}
|
||||
|
||||
@ -3617,6 +3584,21 @@ static void eval_const_expr_implicit_cast(CodeGen *g, AstNode *node, AstNode *ex
|
||||
}
|
||||
}
|
||||
|
||||
static TypeTableEntry *resolve_cast(CodeGen *g, BlockContext *context, AstNode *node,
|
||||
AstNode *expr_node, TypeTableEntry *wanted_type, CastOp op, bool need_alloca)
|
||||
{
|
||||
node->data.fn_call_expr.cast_op = op;
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
if (need_alloca) {
|
||||
if (context->fn_entry) {
|
||||
context->fn_entry->cast_alloca_list.append(node);
|
||||
} else {
|
||||
assert(get_resolved_expr(node)->const_val.ok);
|
||||
}
|
||||
}
|
||||
return wanted_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
AstNode *node)
|
||||
{
|
||||
@ -3642,27 +3624,21 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
|
||||
|
||||
// explicit match or non-const to const
|
||||
if (types_match_const_cast_only(wanted_type, actual_type)) {
|
||||
node->data.fn_call_expr.cast_op = CastOpNoop;
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpNoop, false);
|
||||
}
|
||||
|
||||
// explicit cast from bool to int
|
||||
if (wanted_type->id == TypeTableEntryIdInt &&
|
||||
actual_type->id == TypeTableEntryIdBool)
|
||||
{
|
||||
node->data.fn_call_expr.cast_op = CastOpBoolToInt;
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpBoolToInt, false);
|
||||
}
|
||||
|
||||
// explicit cast from pointer to isize or usize
|
||||
if ((wanted_type == g->builtin_types.entry_isize || wanted_type == g->builtin_types.entry_usize) &&
|
||||
actual_type->id == TypeTableEntryIdPointer)
|
||||
{
|
||||
node->data.fn_call_expr.cast_op = CastOpPtrToInt;
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPtrToInt, false);
|
||||
}
|
||||
|
||||
|
||||
@ -3670,9 +3646,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
|
||||
if (wanted_type->id == TypeTableEntryIdPointer &&
|
||||
(actual_type == g->builtin_types.entry_isize || actual_type == g->builtin_types.entry_usize))
|
||||
{
|
||||
node->data.fn_call_expr.cast_op = CastOpIntToPtr;
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpIntToPtr, false);
|
||||
}
|
||||
|
||||
// explicit widening or shortening cast
|
||||
@ -3681,27 +3655,21 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
|
||||
(wanted_type->id == TypeTableEntryIdFloat &&
|
||||
actual_type->id == TypeTableEntryIdFloat))
|
||||
{
|
||||
node->data.fn_call_expr.cast_op = CastOpWidenOrShorten;
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpWidenOrShorten, false);
|
||||
}
|
||||
|
||||
// explicit cast from int to float
|
||||
if (wanted_type->id == TypeTableEntryIdFloat &&
|
||||
actual_type->id == TypeTableEntryIdInt)
|
||||
{
|
||||
node->data.fn_call_expr.cast_op = CastOpIntToFloat;
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpIntToFloat, false);
|
||||
}
|
||||
|
||||
// explicit cast from float to int
|
||||
if (wanted_type->id == TypeTableEntryIdInt &&
|
||||
actual_type->id == TypeTableEntryIdFloat)
|
||||
{
|
||||
node->data.fn_call_expr.cast_op = CastOpFloatToInt;
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpFloatToInt, false);
|
||||
}
|
||||
|
||||
// explicit cast from fixed size array to unknown size array
|
||||
@ -3712,36 +3680,25 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
|
||||
wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type,
|
||||
actual_type->data.array.child_type))
|
||||
{
|
||||
node->data.fn_call_expr.cast_op = CastOpToUnknownSizeArray;
|
||||
context->cast_alloca_list.append(node);
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpToUnknownSizeArray, true);
|
||||
}
|
||||
|
||||
// explicit cast from pointer to another pointer
|
||||
if (actual_type->id == TypeTableEntryIdPointer &&
|
||||
wanted_type->id == TypeTableEntryIdPointer)
|
||||
{
|
||||
node->data.fn_call_expr.cast_op = CastOpPointerReinterpret;
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPointerReinterpret, false);
|
||||
}
|
||||
|
||||
// explicit cast from child type of maybe type to maybe type
|
||||
if (wanted_type->id == TypeTableEntryIdMaybe) {
|
||||
if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) {
|
||||
node->data.fn_call_expr.cast_op = CastOpMaybeWrap;
|
||||
context->cast_alloca_list.append(node);
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true);
|
||||
} else if (actual_type->id == TypeTableEntryIdNumLitInt ||
|
||||
actual_type->id == TypeTableEntryIdNumLitFloat)
|
||||
{
|
||||
if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.maybe.child_type)) {
|
||||
node->data.fn_call_expr.cast_op = CastOpMaybeWrap;
|
||||
context->cast_alloca_list.append(node);
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true);
|
||||
} else {
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
@ -3751,18 +3708,12 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
|
||||
// explicit cast from child type of error type to error type
|
||||
if (wanted_type->id == TypeTableEntryIdErrorUnion) {
|
||||
if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) {
|
||||
node->data.fn_call_expr.cast_op = CastOpErrorWrap;
|
||||
context->cast_alloca_list.append(node);
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true);
|
||||
} else if (actual_type->id == TypeTableEntryIdNumLitInt ||
|
||||
actual_type->id == TypeTableEntryIdNumLitFloat)
|
||||
{
|
||||
if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.error.child_type)) {
|
||||
node->data.fn_call_expr.cast_op = CastOpErrorWrap;
|
||||
context->cast_alloca_list.append(node);
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true);
|
||||
} else {
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
@ -3773,9 +3724,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
|
||||
if (wanted_type->id == TypeTableEntryIdErrorUnion &&
|
||||
actual_type->id == TypeTableEntryIdPureError)
|
||||
{
|
||||
node->data.fn_call_expr.cast_op = CastOpPureErrorWrap;
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPureErrorWrap, false);
|
||||
}
|
||||
|
||||
// explicit cast from number literal to another type
|
||||
@ -3783,21 +3732,21 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
|
||||
actual_type->id == TypeTableEntryIdNumLitInt)
|
||||
{
|
||||
if (num_lit_fits_in_other_type(g, expr_node, wanted_type)) {
|
||||
CastOp op;
|
||||
if ((actual_type->id == TypeTableEntryIdNumLitFloat &&
|
||||
wanted_type->id == TypeTableEntryIdFloat) ||
|
||||
(actual_type->id == TypeTableEntryIdNumLitInt &&
|
||||
wanted_type->id == TypeTableEntryIdInt))
|
||||
{
|
||||
node->data.fn_call_expr.cast_op = CastOpNoop;
|
||||
op = CastOpNoop;
|
||||
} else if (wanted_type->id == TypeTableEntryIdInt) {
|
||||
node->data.fn_call_expr.cast_op = CastOpFloatToInt;
|
||||
op = CastOpFloatToInt;
|
||||
} else if (wanted_type->id == TypeTableEntryIdFloat) {
|
||||
node->data.fn_call_expr.cast_op = CastOpIntToFloat;
|
||||
op = CastOpIntToFloat;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, op, false);
|
||||
} else {
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
@ -3815,9 +3764,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
|
||||
if (bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count,
|
||||
wanted_type->data.integral.is_signed))
|
||||
{
|
||||
node->data.fn_call_expr.cast_op = CastOpErrToInt;
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
return wanted_type;
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrToInt, false);
|
||||
} else {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name)));
|
||||
@ -4176,7 +4123,7 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
|
||||
}
|
||||
|
||||
if (handle_is_ptr(return_type)) {
|
||||
context->cast_alloca_list.append(node);
|
||||
context->fn_entry->cast_alloca_list.append(node);
|
||||
}
|
||||
|
||||
return return_type;
|
||||
@ -4572,8 +4519,8 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
|
||||
assert(var_node->type == NodeTypeSymbol);
|
||||
Buf *var_name = &var_node->data.symbol_expr.symbol;
|
||||
var_node->block_context = child_context;
|
||||
prong_node->data.switch_prong.var = add_local_var(g, var_node, child_context, var_name,
|
||||
var_type, true);
|
||||
prong_node->data.switch_prong.var = add_local_var(g, var_node, import,
|
||||
child_context, var_name, var_type, true);
|
||||
prong_node->data.switch_prong.var_is_target_expr = var_is_target_expr;
|
||||
}
|
||||
|
||||
@ -4598,6 +4545,9 @@ static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import,
|
||||
normalize_parent_ptrs(node);
|
||||
}
|
||||
|
||||
// TODO follow the blocks to their parents, loop over all of them, set them all to true
|
||||
context->block_exit_paths[BlockExitPathReturn] = true;
|
||||
|
||||
TypeTableEntry *expected_return_type = get_return_type(context);
|
||||
|
||||
switch (node->data.return_expr.kind) {
|
||||
@ -4632,6 +4582,71 @@ static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import,
|
||||
}
|
||||
}
|
||||
|
||||
static void validate_voided_expr(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry) {
|
||||
if (type_entry->id == TypeTableEntryIdMetaType) {
|
||||
add_node_error(g, first_executing_node(source_node), buf_sprintf("expected expression, found type"));
|
||||
} else if (type_entry->id == TypeTableEntryIdErrorUnion) {
|
||||
add_node_error(g, first_executing_node(source_node), buf_sprintf("statement ignores error value"));
|
||||
}
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_defer_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
TypeTableEntry *expected_type, AstNode *node)
|
||||
{
|
||||
if (!context->fn_entry) {
|
||||
add_node_error(g, node, buf_sprintf("defer expression outside function definition"));
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
if (!node->data.defer_expr.expr) {
|
||||
add_node_error(g, node, buf_sprintf("defer expects an expression"));
|
||||
return g->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
|
||||
switch (node->data.defer_expr.kind) {
|
||||
case ReturnKindUnconditional:
|
||||
{
|
||||
TypeTableEntry *resolved_type = analyze_expression(g, import, context, nullptr,
|
||||
node->data.defer_expr.expr);
|
||||
validate_voided_expr(g, node->data.defer_expr.expr, resolved_type);
|
||||
zig_panic("TODO");
|
||||
|
||||
//node->data.defer_expr.index_in_block = context->defer_list.length;
|
||||
//context->defer_list.append(node);
|
||||
return g->builtin_types.entry_void;
|
||||
}
|
||||
case ReturnKindError:
|
||||
{
|
||||
TypeTableEntry *resolved_type = analyze_expression(g, import, context, nullptr,
|
||||
node->data.defer_expr.expr);
|
||||
if (resolved_type->id == TypeTableEntryIdInvalid) {
|
||||
// OK
|
||||
} else if (resolved_type->id == TypeTableEntryIdErrorUnion) {
|
||||
// OK
|
||||
} else {
|
||||
add_node_error(g, node->data.defer_expr.expr,
|
||||
buf_sprintf("expected error type, got '%s'", buf_ptr(&resolved_type->name)));
|
||||
}
|
||||
return g->builtin_types.entry_void;
|
||||
}
|
||||
case ReturnKindMaybe:
|
||||
{
|
||||
TypeTableEntry *resolved_type = analyze_expression(g, import, context, nullptr,
|
||||
node->data.defer_expr.expr);
|
||||
if (resolved_type->id == TypeTableEntryIdInvalid) {
|
||||
// OK
|
||||
} else if (resolved_type->id == TypeTableEntryIdMaybe) {
|
||||
// OK
|
||||
} else {
|
||||
add_node_error(g, node->data.defer_expr.expr,
|
||||
buf_sprintf("expected maybe type, got '%s'", buf_ptr(&resolved_type->name)));
|
||||
}
|
||||
return g->builtin_types.entry_void;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_string_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
TypeTableEntry *expected_type, AstNode *node)
|
||||
{
|
||||
@ -4652,10 +4667,8 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import,
|
||||
for (int i = 0; i < node->data.block.statements.length; i += 1) {
|
||||
AstNode *child = node->data.block.statements.at(i);
|
||||
if (child->type == NodeTypeLabel) {
|
||||
child->block_context = child_context;
|
||||
LabelTableEntry *label_entry = child->data.label.label_entry;
|
||||
assert(label_entry);
|
||||
label_entry->entered_from_fallthrough = (return_type->id != TypeTableEntryIdUnreachable);
|
||||
add_node_error(g, child,
|
||||
buf_sprintf("label and goto not supported yet, see https://github.com/andrewrk/zig/issues/44"));
|
||||
return_type = g->builtin_types.entry_void;
|
||||
continue;
|
||||
}
|
||||
@ -4673,11 +4686,7 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import,
|
||||
TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr;
|
||||
return_type = analyze_expression(g, import, child_context, passed_expected_type, child);
|
||||
if (!is_last) {
|
||||
if (return_type->id == TypeTableEntryIdMetaType) {
|
||||
add_node_error(g, child, buf_sprintf("expected expression, found type"));
|
||||
} else if (return_type->id == TypeTableEntryIdErrorUnion) {
|
||||
add_node_error(g, child, buf_sprintf("statement ignores error value"));
|
||||
}
|
||||
validate_voided_expr(g, child, return_type);
|
||||
}
|
||||
}
|
||||
return return_type;
|
||||
@ -4700,7 +4709,7 @@ static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, Bl
|
||||
}
|
||||
} else {
|
||||
Buf *variable_name = &asm_output->variable_name;
|
||||
VariableTableEntry *var = find_variable(context, variable_name);
|
||||
VariableTableEntry *var = find_variable(context, variable_name, false);
|
||||
if (var) {
|
||||
asm_output->variable = var;
|
||||
return var->type;
|
||||
@ -4719,6 +4728,13 @@ static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, Bl
|
||||
return return_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_goto(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
TypeTableEntry *expected_type, AstNode *node)
|
||||
{
|
||||
add_node_error(g, node, buf_sprintf("goto is broken, see https://github.com/andrewrk/zig/issues/44"));
|
||||
return g->builtin_types.entry_unreachable;
|
||||
}
|
||||
|
||||
// When you call analyze_expression, the node you pass might no longer be the child node
|
||||
// you thought it was due to implicit casting rewriting the AST.
|
||||
static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
@ -4734,24 +4750,16 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
case NodeTypeReturnExpr:
|
||||
return_type = analyze_return_expr(g, import, context, expected_type, node);
|
||||
break;
|
||||
case NodeTypeDeferExpr:
|
||||
return_type = analyze_defer_expr(g, import, context, expected_type, node);
|
||||
break;
|
||||
case NodeTypeVariableDeclaration:
|
||||
analyze_variable_declaration(g, import, context, expected_type, node);
|
||||
return_type = g->builtin_types.entry_void;
|
||||
break;
|
||||
case NodeTypeGoto:
|
||||
{
|
||||
FnTableEntry *fn_table_entry = get_context_fn_entry(context);
|
||||
auto table_entry = fn_table_entry->label_table.maybe_get(&node->data.goto_expr.name);
|
||||
if (table_entry) {
|
||||
node->data.goto_expr.label_entry = table_entry->value;
|
||||
table_entry->value->used = true;
|
||||
} else {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("use of undeclared label '%s'", buf_ptr(&node->data.goto_expr.name)));
|
||||
}
|
||||
return_type = g->builtin_types.entry_unreachable;
|
||||
break;
|
||||
}
|
||||
analyze_goto(g, import, context, expected_type, node);
|
||||
break;
|
||||
case NodeTypeBreak:
|
||||
return_type = analyze_break_expr(g, import, context, expected_type, node);
|
||||
break;
|
||||
@ -4909,7 +4917,7 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
|
||||
add_node_error(g, param_decl_node, buf_sprintf("missing parameter name"));
|
||||
}
|
||||
|
||||
VariableTableEntry *var = add_local_var(g, param_decl_node, context, ¶m_decl->name, type, true);
|
||||
VariableTableEntry *var = add_local_var(g, param_decl_node, import, context, ¶m_decl->name, type, true);
|
||||
var->src_arg_index = i;
|
||||
param_decl_node->data.param_decl.variable = var;
|
||||
|
||||
@ -4920,22 +4928,6 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
|
||||
TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);
|
||||
|
||||
node->data.fn_def.implicit_return_type = block_return_type;
|
||||
|
||||
{
|
||||
auto it = fn_table_entry->label_table.entry_iterator();
|
||||
for (;;) {
|
||||
auto *entry = it.next();
|
||||
if (!entry)
|
||||
break;
|
||||
|
||||
LabelTableEntry *label_entry = entry->value;
|
||||
if (!label_entry->used) {
|
||||
add_node_error(g, label_entry->label_node,
|
||||
buf_sprintf("label '%s' defined but not used",
|
||||
buf_ptr(&label_entry->label_node->data.label.name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) {
|
||||
@ -4964,6 +4956,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeDeferExpr:
|
||||
case NodeTypeRoot:
|
||||
case NodeTypeBlock:
|
||||
case NodeTypeBinOpExpr:
|
||||
@ -5028,7 +5021,7 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode
|
||||
Buf *name = &node->data.symbol_expr.symbol;
|
||||
auto table_entry = g->primitive_type_table.maybe_get(name);
|
||||
if (!table_entry) {
|
||||
table_entry = import->block_context->type_table.maybe_get(name);
|
||||
table_entry = import->type_table.maybe_get(name);
|
||||
}
|
||||
if (!table_entry || !type_is_complete(table_entry->value)) {
|
||||
decl_node->deps.put(name, node);
|
||||
@ -5046,6 +5039,9 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode
|
||||
case NodeTypeReturnExpr:
|
||||
collect_expr_decl_deps(g, import, node->data.return_expr.expr, decl_node);
|
||||
break;
|
||||
case NodeTypeDeferExpr:
|
||||
collect_expr_decl_deps(g, import, node->data.defer_expr.expr, decl_node);
|
||||
break;
|
||||
case NodeTypePrefixOpExpr:
|
||||
collect_expr_decl_deps(g, import, node->data.prefix_op_expr.primary_expr, decl_node);
|
||||
break;
|
||||
@ -5196,7 +5192,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
|
||||
Buf *name = &node->data.struct_decl.name;
|
||||
auto table_entry = g->primitive_type_table.maybe_get(name);
|
||||
if (!table_entry) {
|
||||
table_entry = import->block_context->type_table.maybe_get(name);
|
||||
table_entry = import->type_table.maybe_get(name);
|
||||
}
|
||||
if (table_entry) {
|
||||
node->data.struct_decl.type_entry = table_entry->value;
|
||||
@ -5210,20 +5206,20 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
|
||||
node->data.struct_decl.kind, node, buf_ptr(name));
|
||||
}
|
||||
|
||||
import->block_context->type_table.put(&entry->name, entry);
|
||||
import->type_table.put(&entry->name, entry);
|
||||
node->data.struct_decl.type_entry = entry;
|
||||
|
||||
bool is_pub = (node->data.struct_decl.visib_mod != VisibModPrivate);
|
||||
if (is_pub) {
|
||||
for (int i = 0; i < import->importers.length; i += 1) {
|
||||
ImporterInfo importer = import->importers.at(i);
|
||||
auto table_entry = importer.import->block_context->type_table.maybe_get(&entry->name);
|
||||
auto table_entry = importer.import->type_table.maybe_get(&entry->name);
|
||||
if (table_entry) {
|
||||
add_node_error(g, importer.source_node,
|
||||
buf_sprintf("import of type '%s' overrides existing definition",
|
||||
buf_ptr(&entry->name)));
|
||||
} else {
|
||||
importer.import->block_context->type_table.put(&entry->name, entry);
|
||||
importer.import->type_table.put(&entry->name, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5365,6 +5361,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeDeferExpr:
|
||||
case NodeTypeBlock:
|
||||
case NodeTypeBinOpExpr:
|
||||
case NodeTypeUnwrapErrorExpr:
|
||||
@ -5555,6 +5552,8 @@ Expr *get_resolved_expr(AstNode *node) {
|
||||
switch (node->type) {
|
||||
case NodeTypeReturnExpr:
|
||||
return &node->data.return_expr.resolved_expr;
|
||||
case NodeTypeDeferExpr:
|
||||
return &node->data.defer_expr.resolved_expr;
|
||||
case NodeTypeBinOpExpr:
|
||||
return &node->data.bin_op_expr.resolved_expr;
|
||||
case NodeTypeUnwrapErrorExpr:
|
||||
@ -5653,6 +5652,7 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
|
||||
return &node->data.type_decl.top_level_decl;
|
||||
case NodeTypeNumberLiteral:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeDeferExpr:
|
||||
case NodeTypeBinOpExpr:
|
||||
case NodeTypeUnwrapErrorExpr:
|
||||
case NodeTypePrefixOpExpr:
|
||||
|
@ -14,8 +14,6 @@ void semantic_analyze(CodeGen *g);
|
||||
ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);
|
||||
TypeTableEntry *new_type_table_entry(TypeTableEntryId id);
|
||||
TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const);
|
||||
VariableTableEntry *find_variable(BlockContext *context, Buf *name);
|
||||
TypeTableEntry *find_container(BlockContext *context, Buf *name);
|
||||
BlockContext *new_block_context(AstNode *node, BlockContext *parent);
|
||||
Expr *get_resolved_expr(AstNode *node);
|
||||
TopLevelDecl *get_resolved_top_level_decl(AstNode *node);
|
||||
|
@ -122,6 +122,8 @@ static const char *node_type_str(NodeType node_type) {
|
||||
return "Directive";
|
||||
case NodeTypeReturnExpr:
|
||||
return "ReturnExpr";
|
||||
case NodeTypeDeferExpr:
|
||||
return "DeferExpr";
|
||||
case NodeTypeVariableDeclaration:
|
||||
return "VariableDeclaration";
|
||||
case NodeTypeTypeDecl:
|
||||
@ -259,6 +261,14 @@ void ast_print(FILE *f, AstNode *node, int indent) {
|
||||
ast_print(f, node->data.return_expr.expr, indent + 2);
|
||||
break;
|
||||
}
|
||||
case NodeTypeDeferExpr:
|
||||
{
|
||||
const char *prefix_str = return_prefix_str(node->data.defer_expr.kind);
|
||||
fprintf(f, "%s%s\n", prefix_str, node_type_str(node->type));
|
||||
if (node->data.defer_expr.expr)
|
||||
ast_print(f, node->data.defer_expr.expr, indent + 2);
|
||||
break;
|
||||
}
|
||||
case NodeTypeVariableDeclaration:
|
||||
{
|
||||
Buf *name_buf = &node->data.variable_declaration.symbol;
|
||||
@ -620,6 +630,8 @@ static void render_node(AstRender *ar, AstNode *node) {
|
||||
break;
|
||||
case NodeTypeReturnExpr:
|
||||
zig_panic("TODO");
|
||||
case NodeTypeDeferExpr:
|
||||
zig_panic("TODO");
|
||||
case NodeTypeVariableDeclaration:
|
||||
{
|
||||
const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);
|
||||
|
159
src/codegen.cpp
159
src/codegen.cpp
@ -1669,6 +1669,15 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_defer_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeDeferExpr);
|
||||
|
||||
zig_panic("TODO");
|
||||
//node->block_context->cur_defer_index = node->data.defer_expr.index_in_block;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMValueRef cond_value,
|
||||
AstNode *then_node, AstNode *else_node)
|
||||
{
|
||||
@ -1794,6 +1803,21 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
|
||||
static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) {
|
||||
assert(block_node->type == NodeTypeBlock);
|
||||
|
||||
/* TODO
|
||||
BlockContext *block_context = block_node->data.block.block_context;
|
||||
if (block_context->defer_list.length > 0) {
|
||||
LLVMBasicBlockRef exit_scope_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DeferExitScope");
|
||||
|
||||
for (int i = 0; i < block_context->defer_list.length; i += 1) {
|
||||
AstNode *defer_node = block_context->defer_list.at(i);
|
||||
defer_node->data.defer_expr.basic_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DeferExpr");
|
||||
LLVMPositionBuilderAtEnd(g->builder, body_block);
|
||||
}
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ?);
|
||||
}
|
||||
*/
|
||||
|
||||
LLVMValueRef return_value;
|
||||
for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
|
||||
AstNode *statement_node = block_node->data.block.statements.at(i);
|
||||
@ -2451,6 +2475,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||
return gen_unwrap_err_expr(g, node);
|
||||
case NodeTypeReturnExpr:
|
||||
return gen_return_expr(g, node);
|
||||
case NodeTypeDeferExpr:
|
||||
return gen_defer_expr(g, node);
|
||||
case NodeTypeVariableDeclaration:
|
||||
return gen_var_decl_expr(g, node);
|
||||
case NodeTypePrefixOpExpr:
|
||||
@ -2478,24 +2504,13 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||
case NodeTypeBlock:
|
||||
return gen_block(g, node, nullptr);
|
||||
case NodeTypeGoto:
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildBr(g->builder, node->data.goto_expr.label_entry->basic_block);
|
||||
zig_unreachable();
|
||||
case NodeTypeBreak:
|
||||
return gen_break(g, node);
|
||||
case NodeTypeContinue:
|
||||
return gen_continue(g, node);
|
||||
case NodeTypeLabel:
|
||||
{
|
||||
LabelTableEntry *label_entry = node->data.label.label_entry;
|
||||
assert(label_entry);
|
||||
LLVMBasicBlockRef basic_block = label_entry->basic_block;
|
||||
if (label_entry->entered_from_fallthrough) {
|
||||
add_debug_source_node(g, node);
|
||||
LLVMBuildBr(g->builder, basic_block);
|
||||
}
|
||||
LLVMPositionBuilderAtEnd(g->builder, basic_block);
|
||||
return nullptr;
|
||||
}
|
||||
zig_unreachable();
|
||||
case NodeTypeContainerInitExpr:
|
||||
return gen_container_init_expr(g, node);
|
||||
case NodeTypeSwitchExpr:
|
||||
@ -2532,19 +2547,6 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static void build_label_blocks(CodeGen *g, AstNode *block_node) {
|
||||
assert(block_node->type == NodeTypeBlock);
|
||||
for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
|
||||
AstNode *label_node = block_node->data.block.statements.at(i);
|
||||
if (label_node->type != NodeTypeLabel)
|
||||
continue;
|
||||
|
||||
Buf *name = &label_node->data.label.name;
|
||||
label_node->data.label.label_entry->basic_block = LLVMAppendBasicBlock(
|
||||
g->cur_fn->fn_value, buf_ptr(name));
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val) {
|
||||
assert(const_val->ok);
|
||||
|
||||
@ -2977,11 +2979,7 @@ static void do_code_gen(CodeGen *g) {
|
||||
LLVMPositionBuilderAtEnd(g->builder, entry_block);
|
||||
|
||||
|
||||
AstNode *body_node = fn_def_node->data.fn_def.body;
|
||||
build_label_blocks(g, body_node);
|
||||
|
||||
// Set up debug info for blocks and variables and
|
||||
// allocate all local variables
|
||||
// Set up debug info for blocks
|
||||
for (int bc_i = 0; bc_i < fn_table_entry->all_block_contexts.length; bc_i += 1) {
|
||||
BlockContext *block_context = fn_table_entry->all_block_contexts.at(bc_i);
|
||||
|
||||
@ -2994,59 +2992,45 @@ static void do_code_gen(CodeGen *g) {
|
||||
block_context->di_scope = LLVMZigLexicalBlockToScope(di_block);
|
||||
}
|
||||
|
||||
for (int var_i = 0; var_i < block_context->variable_list.length; var_i += 1) {
|
||||
VariableTableEntry *var = block_context->variable_list.at(var_i);
|
||||
|
||||
if (!type_has_bits(var->type)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned tag;
|
||||
unsigned arg_no;
|
||||
TypeTableEntry *gen_type;
|
||||
if (block_context->node->type == NodeTypeFnDef) {
|
||||
tag = LLVMZigTag_DW_arg_variable();
|
||||
arg_no = var->gen_arg_index + 1;
|
||||
// create debug variable declarations for variables and allocate all local variables
|
||||
for (int var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) {
|
||||
VariableTableEntry *var = fn_table_entry->variable_list.at(var_i);
|
||||
|
||||
var->is_ptr = false;
|
||||
assert(var->gen_arg_index >= 0);
|
||||
var->value_ref = LLVMGetParam(fn, var->gen_arg_index);
|
||||
|
||||
gen_type = fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index].type;
|
||||
} else {
|
||||
tag = LLVMZigTag_DW_auto_variable();
|
||||
arg_no = 0;
|
||||
|
||||
add_debug_source_node(g, var->decl_node);
|
||||
var->value_ref = LLVMBuildAlloca(g->builder, var->type->type_ref, buf_ptr(&var->name));
|
||||
uint64_t align_bytes = LLVMABISizeOfType(g->target_data_ref, var->type->type_ref);
|
||||
LLVMSetAlignment(var->value_ref, align_bytes);
|
||||
|
||||
gen_type = var->type;
|
||||
}
|
||||
|
||||
var->di_loc_var = LLVMZigCreateLocalVariable(g->dbuilder, tag,
|
||||
block_context->di_scope, buf_ptr(&var->name),
|
||||
import->di_file, var->decl_node->line + 1,
|
||||
gen_type->di_type, !g->strip_debug_symbols, 0, arg_no);
|
||||
if (!type_has_bits(var->type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// allocate structs which are the result of casts
|
||||
for (int cea_i = 0; cea_i < block_context->cast_alloca_list.length; cea_i += 1) {
|
||||
AstNode *fn_call_node = block_context->cast_alloca_list.at(cea_i);
|
||||
add_debug_source_node(g, fn_call_node);
|
||||
Expr *expr = &fn_call_node->data.fn_call_expr.resolved_expr;
|
||||
fn_call_node->data.fn_call_expr.tmp_ptr = LLVMBuildAlloca(g->builder,
|
||||
expr->type_entry->type_ref, "");
|
||||
unsigned tag;
|
||||
unsigned arg_no;
|
||||
TypeTableEntry *gen_type;
|
||||
if (var->block_context->node->type == NodeTypeFnDef) {
|
||||
tag = LLVMZigTag_DW_arg_variable();
|
||||
arg_no = var->gen_arg_index + 1;
|
||||
|
||||
var->is_ptr = false;
|
||||
assert(var->gen_arg_index >= 0);
|
||||
var->value_ref = LLVMGetParam(fn, var->gen_arg_index);
|
||||
|
||||
gen_type = fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index].type;
|
||||
} else {
|
||||
tag = LLVMZigTag_DW_auto_variable();
|
||||
arg_no = 0;
|
||||
|
||||
add_debug_source_node(g, var->decl_node);
|
||||
var->value_ref = LLVMBuildAlloca(g->builder, var->type->type_ref, buf_ptr(&var->name));
|
||||
uint64_t align_bytes = LLVMABISizeOfType(g->target_data_ref, var->type->type_ref);
|
||||
LLVMSetAlignment(var->value_ref, align_bytes);
|
||||
|
||||
gen_type = var->type;
|
||||
}
|
||||
|
||||
// allocate structs which are struct value expressions
|
||||
for (int alloca_i = 0; alloca_i < block_context->struct_val_expr_alloca_list.length; alloca_i += 1) {
|
||||
StructValExprCodeGen *struct_val_expr_node = block_context->struct_val_expr_alloca_list.at(alloca_i);
|
||||
add_debug_source_node(g, struct_val_expr_node->source_node);
|
||||
struct_val_expr_node->ptr = LLVMBuildAlloca(g->builder,
|
||||
struct_val_expr_node->type_entry->type_ref, "");
|
||||
}
|
||||
var->di_loc_var = LLVMZigCreateLocalVariable(g->dbuilder, tag,
|
||||
var->block_context->di_scope, buf_ptr(&var->name),
|
||||
import->di_file, var->decl_node->line + 1,
|
||||
gen_type->di_type, !g->strip_debug_symbols, 0, arg_no);
|
||||
}
|
||||
|
||||
// create debug variable declarations for parameters
|
||||
@ -3069,6 +3053,23 @@ static void do_code_gen(CodeGen *g) {
|
||||
entry_block);
|
||||
}
|
||||
|
||||
// allocate structs which are the result of casts
|
||||
for (int cea_i = 0; cea_i < fn_table_entry->cast_alloca_list.length; cea_i += 1) {
|
||||
AstNode *fn_call_node = fn_table_entry->cast_alloca_list.at(cea_i);
|
||||
add_debug_source_node(g, fn_call_node);
|
||||
Expr *expr = &fn_call_node->data.fn_call_expr.resolved_expr;
|
||||
fn_call_node->data.fn_call_expr.tmp_ptr = LLVMBuildAlloca(g->builder,
|
||||
expr->type_entry->type_ref, "");
|
||||
}
|
||||
|
||||
// allocate structs which are struct value expressions
|
||||
for (int alloca_i = 0; alloca_i < fn_table_entry->struct_val_expr_alloca_list.length; alloca_i += 1) {
|
||||
StructValExprCodeGen *struct_val_expr_node = fn_table_entry->struct_val_expr_alloca_list.at(alloca_i);
|
||||
add_debug_source_node(g, struct_val_expr_node->source_node);
|
||||
struct_val_expr_node->ptr = LLVMBuildAlloca(g->builder,
|
||||
struct_val_expr_node->type_entry->type_ref, "");
|
||||
}
|
||||
|
||||
TypeTableEntry *implicit_return_type = fn_def_node->data.fn_def.implicit_return_type;
|
||||
gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type);
|
||||
|
||||
@ -3524,6 +3525,8 @@ void codegen_parseh(CodeGen *g, Buf *src_dirname, Buf *src_basename, Buf *source
|
||||
import->source_code = source_code;
|
||||
import->path = full_path;
|
||||
import->fn_table.init(32);
|
||||
import->type_table.init(8);
|
||||
import->error_table.init(8);
|
||||
g->root_import = import;
|
||||
|
||||
init(g, full_path);
|
||||
@ -3614,6 +3617,8 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path,
|
||||
import_entry->line_offsets = tokenization.line_offsets;
|
||||
import_entry->path = full_path;
|
||||
import_entry->fn_table.init(32);
|
||||
import_entry->type_table.init(8);
|
||||
import_entry->error_table.init(8);
|
||||
|
||||
import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color,
|
||||
&g->next_node_index);
|
||||
|
@ -1635,20 +1635,24 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda
|
||||
|
||||
/*
|
||||
ReturnExpression : option("%" | "?") "return" option(Expression)
|
||||
DeferExpression = option("%" | "?") "defer" option(Expression)
|
||||
*/
|
||||
static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
static AstNode *ast_parse_return_or_defer_expr(ParseContext *pc, int *token_index) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
|
||||
NodeType node_type;
|
||||
ReturnKind kind;
|
||||
|
||||
if (token->id == TokenIdPercent) {
|
||||
Token *next_token = &pc->tokens->at(*token_index + 1);
|
||||
if (next_token->id == TokenIdKeywordReturn) {
|
||||
kind = ReturnKindError;
|
||||
node_type = NodeTypeReturnExpr;
|
||||
*token_index += 2;
|
||||
} else if (next_token->id == TokenIdKeywordDefer) {
|
||||
kind = ReturnKindError;
|
||||
node_type = NodeTypeDeferExpr;
|
||||
*token_index += 2;
|
||||
} else if (mandatory) {
|
||||
ast_expect_token(pc, next_token, TokenIdKeywordReturn);
|
||||
zig_unreachable();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
@ -1656,24 +1660,28 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m
|
||||
Token *next_token = &pc->tokens->at(*token_index + 1);
|
||||
if (next_token->id == TokenIdKeywordReturn) {
|
||||
kind = ReturnKindMaybe;
|
||||
node_type = NodeTypeReturnExpr;
|
||||
*token_index += 2;
|
||||
} else if (next_token->id == TokenIdKeywordDefer) {
|
||||
kind = ReturnKindMaybe;
|
||||
node_type = NodeTypeDeferExpr;
|
||||
*token_index += 2;
|
||||
} else if (mandatory) {
|
||||
ast_expect_token(pc, next_token, TokenIdKeywordReturn);
|
||||
zig_unreachable();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (token->id == TokenIdKeywordReturn) {
|
||||
kind = ReturnKindUnconditional;
|
||||
node_type = NodeTypeReturnExpr;
|
||||
*token_index += 1;
|
||||
} else if (token->id == TokenIdKeywordDefer) {
|
||||
kind = ReturnKindUnconditional;
|
||||
node_type = NodeTypeDeferExpr;
|
||||
*token_index += 1;
|
||||
} else if (mandatory) {
|
||||
ast_expect_token(pc, token, TokenIdKeywordReturn);
|
||||
zig_unreachable();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, token);
|
||||
AstNode *node = ast_create_node(pc, node_type, token);
|
||||
node->data.return_expr.kind = kind;
|
||||
node->data.return_expr.expr = ast_parse_expression(pc, token_index, false);
|
||||
|
||||
@ -2060,7 +2068,7 @@ NonBlockExpression : ReturnExpression | AssignmentExpression
|
||||
static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
|
||||
AstNode *return_expr = ast_parse_return_expr(pc, token_index, false);
|
||||
AstNode *return_expr = ast_parse_return_or_defer_expr(pc, token_index);
|
||||
if (return_expr)
|
||||
return return_expr;
|
||||
|
||||
@ -2695,6 +2703,9 @@ void normalize_parent_ptrs(AstNode *node) {
|
||||
case NodeTypeReturnExpr:
|
||||
set_field(&node->data.return_expr.expr);
|
||||
break;
|
||||
case NodeTypeDeferExpr:
|
||||
set_field(&node->data.defer_expr.expr);
|
||||
break;
|
||||
case NodeTypeVariableDeclaration:
|
||||
set_list_fields(node->data.variable_declaration.directives);
|
||||
set_field(&node->data.variable_declaration.type);
|
||||
|
@ -102,6 +102,7 @@ const char * zig_keywords[] = {
|
||||
"pub", "export", "import", "c_import", "if", "else", "goto", "asm",
|
||||
"volatile", "struct", "enum", "while", "for", "continue", "break",
|
||||
"null", "noalias", "switch", "undefined", "error", "type", "inline",
|
||||
"defer",
|
||||
};
|
||||
|
||||
bool is_zig_keyword(Buf *buf) {
|
||||
@ -275,6 +276,8 @@ static void end_token(Tokenize *t) {
|
||||
t->cur_tok->id = TokenIdKeywordType;
|
||||
} else if (mem_eql_str(token_mem, token_len, "inline")) {
|
||||
t->cur_tok->id = TokenIdKeywordInline;
|
||||
} else if (mem_eql_str(token_mem, token_len, "defer")) {
|
||||
t->cur_tok->id = TokenIdKeywordDefer;
|
||||
}
|
||||
|
||||
t->cur_tok = nullptr;
|
||||
@ -1090,6 +1093,7 @@ const char * token_name(TokenId id) {
|
||||
case TokenIdKeywordError: return "error";
|
||||
case TokenIdKeywordType: return "type";
|
||||
case TokenIdKeywordInline: return "inline";
|
||||
case TokenIdKeywordDefer: return "defer";
|
||||
case TokenIdLParen: return "(";
|
||||
case TokenIdRParen: return ")";
|
||||
case TokenIdComma: return ",";
|
||||
|
@ -42,6 +42,7 @@ enum TokenId {
|
||||
TokenIdKeywordError,
|
||||
TokenIdKeywordType,
|
||||
TokenIdKeywordInline,
|
||||
TokenIdKeywordDefer,
|
||||
TokenIdLParen,
|
||||
TokenIdRParen,
|
||||
TokenIdComma,
|
||||
|
@ -50,6 +50,8 @@ pub struct OutStream {
|
||||
fd: isize,
|
||||
buffer: [buffer_size]u8,
|
||||
index: isize,
|
||||
// TODO remove this. let the user flush at will.
|
||||
// for stderr the user can use printf
|
||||
buffered: bool,
|
||||
|
||||
pub fn print_str(os: &OutStream, str: []const u8) -> %isize {
|
||||
|
@ -259,25 +259,6 @@ pub fn main(args: [][]u8) -> %void {
|
||||
}
|
||||
)SOURCE", "pass\n");
|
||||
|
||||
add_simple_case("goto", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
fn loop(a : i32) {
|
||||
if (a == 0) {
|
||||
goto done;
|
||||
}
|
||||
%%stdout.printf("loop\n");
|
||||
loop(a - 1);
|
||||
|
||||
done:
|
||||
return;
|
||||
}
|
||||
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
loop(3);
|
||||
}
|
||||
)SOURCE", "loop\nloop\nloop\n");
|
||||
|
||||
add_simple_case("local variables", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
@ -1613,16 +1594,6 @@ fn a() {
|
||||
".tmp_source.zig:3:5: error: use of undeclared identifier 'b'",
|
||||
".tmp_source.zig:4:5: error: use of undeclared identifier 'c'");
|
||||
|
||||
add_compile_fail_case("goto cause unreachable code", R"SOURCE(
|
||||
fn a() {
|
||||
goto done;
|
||||
b();
|
||||
done:
|
||||
return;
|
||||
}
|
||||
fn b() {}
|
||||
)SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code");
|
||||
|
||||
add_compile_fail_case("parameter redeclaration", R"SOURCE(
|
||||
fn f(a : i32, a : i32) {
|
||||
}
|
||||
@ -1670,12 +1641,6 @@ fn f() {
|
||||
fn f(a : unreachable) {}
|
||||
)SOURCE", 1, ".tmp_source.zig:2:10: error: parameter of type 'unreachable' not allowed");
|
||||
|
||||
add_compile_fail_case("unused label", R"SOURCE(
|
||||
fn f() {
|
||||
a_label:
|
||||
}
|
||||
)SOURCE", 1, ".tmp_source.zig:3:1: error: label 'a_label' defined but not used");
|
||||
|
||||
add_compile_fail_case("bad assignment target", R"SOURCE(
|
||||
fn f() {
|
||||
3 = 3;
|
||||
|
Loading…
Reference in New Issue
Block a user