mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 21:08:36 +00:00
add array access syntax
This commit is contained in:
parent
2f0e4e9cb2
commit
75efc31329
12
README.md
12
README.md
@ -64,6 +64,8 @@ compromises backward compatibility.
|
||||
* main function with command line arguments
|
||||
* void pointer constant
|
||||
* sizeof
|
||||
* address of operator
|
||||
* global variables
|
||||
* static initializers
|
||||
* assert
|
||||
* function pointers
|
||||
@ -202,9 +204,13 @@ MultiplyOperator : token(Star) | token(Slash) | token(Percent)
|
||||
|
||||
CastExpression : PrefixOpExpression token(as) Type | PrefixOpExpression
|
||||
|
||||
PrefixOpExpression : PrefixOp FnCallExpression | FnCallExpression
|
||||
PrefixOpExpression : PrefixOp SuffixOpExpression | SuffixOpExpression
|
||||
|
||||
FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma)) token(RParen) | PrimaryExpression
|
||||
SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression)
|
||||
|
||||
FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
|
||||
|
||||
ArrayAccessExpression : token(LBracket) Expression token(RBracket)
|
||||
|
||||
PrefixOp : token(Not) | token(Dash) | token(Tilde)
|
||||
|
||||
@ -220,7 +226,7 @@ KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False)
|
||||
## Operator Precedence
|
||||
|
||||
```
|
||||
x()
|
||||
x() x[]
|
||||
!x -x ~x
|
||||
as
|
||||
* / %
|
||||
|
||||
16
example/arrays/arrays.zig
Normal file
16
example/arrays/arrays.zig
Normal file
@ -0,0 +1,16 @@
|
||||
export executable "arrays";
|
||||
|
||||
#link("c")
|
||||
extern {
|
||||
fn puts(s: *const u8) -> i32;
|
||||
fn exit(code: i32) -> unreachable;
|
||||
}
|
||||
|
||||
export fn _start() -> unreachable {
|
||||
let mut array : [i32; 10];
|
||||
|
||||
array[4] = array[1] + 5;
|
||||
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@ -6,11 +6,45 @@
|
||||
*/
|
||||
|
||||
#include "analyze.hpp"
|
||||
#include "semantic_info.hpp"
|
||||
#include "error.hpp"
|
||||
#include "zig_llvm.hpp"
|
||||
#include "os.hpp"
|
||||
|
||||
static AstNode *first_executing_node(AstNode *node) {
|
||||
switch (node->type) {
|
||||
case NodeTypeFnCallExpr:
|
||||
return first_executing_node(node->data.fn_call_expr.fn_ref_expr);
|
||||
case NodeTypeRoot:
|
||||
case NodeTypeRootExportDecl:
|
||||
case NodeTypeFnProto:
|
||||
case NodeTypeFnDef:
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeType:
|
||||
case NodeTypeBlock:
|
||||
case NodeTypeExternBlock:
|
||||
case NodeTypeDirective:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeVariableDeclaration:
|
||||
case NodeTypeBinOpExpr:
|
||||
case NodeTypeCastExpr:
|
||||
case NodeTypeNumberLiteral:
|
||||
case NodeTypeStringLiteral:
|
||||
case NodeTypeUnreachable:
|
||||
case NodeTypeSymbol:
|
||||
case NodeTypePrefixOpExpr:
|
||||
case NodeTypeArrayAccessExpr:
|
||||
case NodeTypeUse:
|
||||
case NodeTypeVoid:
|
||||
case NodeTypeBoolLiteral:
|
||||
case NodeTypeIfExpr:
|
||||
case NodeTypeLabel:
|
||||
case NodeTypeGoto:
|
||||
return node;
|
||||
}
|
||||
zig_panic("unreachable");
|
||||
}
|
||||
|
||||
void add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
|
||||
ErrorMsg *err = allocate<ErrorMsg>(1);
|
||||
err->line_start = node->line;
|
||||
@ -48,9 +82,10 @@ static void set_root_export_version(CodeGen *g, Buf *version_buf, AstNode *node)
|
||||
}
|
||||
}
|
||||
|
||||
TypeTableEntry *new_type_table_entry() {
|
||||
TypeTableEntry *new_type_table_entry(TypeTableEntryId id) {
|
||||
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
|
||||
entry->arrays_by_size.init(2);
|
||||
entry->id = id;
|
||||
return entry;
|
||||
}
|
||||
|
||||
@ -61,7 +96,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
|
||||
if (*parent_pointer) {
|
||||
return *parent_pointer;
|
||||
} else {
|
||||
TypeTableEntry *entry = new_type_table_entry();
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer);
|
||||
entry->type_ref = LLVMPointerType(child_type->type_ref, 0);
|
||||
buf_resize(&entry->name, 0);
|
||||
buf_appendf(&entry->name, "*%s %s", is_const ? "const" : "mut", buf_ptr(&child_type->name));
|
||||
@ -80,7 +115,7 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in
|
||||
if (existing_entry) {
|
||||
return existing_entry->value;
|
||||
} else {
|
||||
TypeTableEntry *entry = new_type_table_entry();
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);
|
||||
entry->type_ref = LLVMArrayType(child_type->type_ref, array_size);
|
||||
buf_resize(&entry->name, 0);
|
||||
buf_appendf(&entry->name, "[%s; %d]", buf_ptr(&child_type->name), array_size);
|
||||
@ -357,6 +392,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
case NodeTypeBlock:
|
||||
case NodeTypeBinOpExpr:
|
||||
case NodeTypeFnCallExpr:
|
||||
case NodeTypeArrayAccessExpr:
|
||||
case NodeTypeNumberLiteral:
|
||||
case NodeTypeStringLiteral:
|
||||
case NodeTypeUnreachable:
|
||||
@ -466,7 +502,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
// ignore void statements once we enter unreachable land.
|
||||
continue;
|
||||
}
|
||||
add_node_error(g, child, buf_sprintf("unreachable code"));
|
||||
add_node_error(g, first_executing_node(child), buf_sprintf("unreachable code"));
|
||||
break;
|
||||
}
|
||||
return_type = analyze_expression(g, import, child_context, nullptr, child);
|
||||
@ -641,14 +677,21 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
|
||||
case NodeTypeFnCallExpr:
|
||||
{
|
||||
Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr);
|
||||
AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
|
||||
if (fn_ref_expr->type != NodeTypeSymbol) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("function pointers not allowed"));
|
||||
break;
|
||||
}
|
||||
|
||||
Buf *name = &fn_ref_expr->data.symbol;
|
||||
|
||||
auto entry = import->fn_table.maybe_get(name);
|
||||
if (!entry)
|
||||
entry = g->fn_table.maybe_get(name);
|
||||
|
||||
if (!entry) {
|
||||
add_node_error(g, node,
|
||||
add_node_error(g, fn_ref_expr,
|
||||
buf_sprintf("undefined function: '%s'", buf_ptr(name)));
|
||||
// still analyze the parameters, even though we don't know what to expect
|
||||
for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
|
||||
@ -691,6 +734,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeTypeArrayAccessExpr:
|
||||
{
|
||||
// here we are always reading the array
|
||||
TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr,
|
||||
node->data.array_access_expr.array_ref_expr);
|
||||
if (lhs_type->id == TypeTableEntryIdArray) {
|
||||
zig_panic("TODO");
|
||||
} else {
|
||||
add_node_error(g, node, buf_sprintf("array access of non-array"));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case NodeTypeNumberLiteral:
|
||||
// TODO: generic literal int type
|
||||
return_type = g->builtin_types.entry_i32;
|
||||
@ -897,6 +953,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
|
||||
case NodeTypeBlock:
|
||||
case NodeTypeBinOpExpr:
|
||||
case NodeTypeFnCallExpr:
|
||||
case NodeTypeArrayAccessExpr:
|
||||
case NodeTypeNumberLiteral:
|
||||
case NodeTypeStringLiteral:
|
||||
case NodeTypeUnreachable:
|
||||
|
||||
211
src/analyze.hpp
211
src/analyze.hpp
@ -8,17 +8,216 @@
|
||||
#ifndef ZIG_ANALYZE_HPP
|
||||
#define ZIG_ANALYZE_HPP
|
||||
|
||||
struct CodeGen;
|
||||
struct AstNode;
|
||||
struct Buf;
|
||||
#include "codegen.hpp"
|
||||
#include "hash_map.hpp"
|
||||
#include "zig_llvm.hpp"
|
||||
#include "errmsg.hpp"
|
||||
|
||||
struct TypeTableEntry;
|
||||
struct LocalVariableTableEntry;
|
||||
struct FnTableEntry;
|
||||
struct BlockContext;
|
||||
struct TypeTableEntry;
|
||||
|
||||
struct TypeTableEntryPointer {
|
||||
TypeTableEntry *pointer_child;
|
||||
bool pointer_is_const;
|
||||
};
|
||||
|
||||
struct TypeTableEntryInt {
|
||||
bool is_signed;
|
||||
};
|
||||
|
||||
enum TypeTableEntryId {
|
||||
TypeTableEntryIdInvalid,
|
||||
TypeTableEntryIdVoid,
|
||||
TypeTableEntryIdBool,
|
||||
TypeTableEntryIdUnreachable,
|
||||
TypeTableEntryIdInt,
|
||||
TypeTableEntryIdFloat,
|
||||
TypeTableEntryIdPointer,
|
||||
TypeTableEntryIdArray,
|
||||
};
|
||||
|
||||
struct TypeTableEntry {
|
||||
TypeTableEntryId id;
|
||||
|
||||
LLVMTypeRef type_ref;
|
||||
LLVMZigDIType *di_type;
|
||||
uint64_t size_in_bits;
|
||||
uint64_t align_in_bits;
|
||||
|
||||
Buf name;
|
||||
|
||||
union {
|
||||
TypeTableEntryPointer pointer;
|
||||
TypeTableEntryInt integral;
|
||||
} data;
|
||||
|
||||
// use these fields to make sure we don't duplicate type table entries for the same type
|
||||
TypeTableEntry *pointer_const_parent;
|
||||
TypeTableEntry *pointer_mut_parent;
|
||||
HashMap<int, TypeTableEntry *, int_hash, int_eq> arrays_by_size;
|
||||
|
||||
};
|
||||
|
||||
struct ImportTableEntry {
|
||||
AstNode *root;
|
||||
Buf *path; // relative to root_source_dir
|
||||
LLVMZigDIFile *di_file;
|
||||
Buf *source_code;
|
||||
ZigList<int> *line_offsets;
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
struct FnTableEntry {
|
||||
LLVMValueRef fn_value;
|
||||
AstNode *proto_node;
|
||||
AstNode *fn_def_node;
|
||||
bool is_extern;
|
||||
bool internal_linkage;
|
||||
unsigned calling_convention;
|
||||
ImportTableEntry *import_entry;
|
||||
|
||||
// reminder: hash tables must be initialized before use
|
||||
HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
|
||||
};
|
||||
|
||||
struct CodeGen {
|
||||
LLVMModuleRef module;
|
||||
ZigList<ErrorMsg*> errors;
|
||||
LLVMBuilderRef builder;
|
||||
LLVMZigDIBuilder *dbuilder;
|
||||
LLVMZigDICompileUnit *compile_unit;
|
||||
|
||||
// reminder: hash tables must be initialized before use
|
||||
HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
|
||||
HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table;
|
||||
HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
|
||||
HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table;
|
||||
HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
|
||||
|
||||
struct {
|
||||
TypeTableEntry *entry_bool;
|
||||
TypeTableEntry *entry_u8;
|
||||
TypeTableEntry *entry_i32;
|
||||
TypeTableEntry *entry_f32;
|
||||
TypeTableEntry *entry_string_literal;
|
||||
TypeTableEntry *entry_void;
|
||||
TypeTableEntry *entry_unreachable;
|
||||
TypeTableEntry *entry_invalid;
|
||||
} builtin_types;
|
||||
|
||||
LLVMTargetDataRef target_data_ref;
|
||||
unsigned pointer_size_bytes;
|
||||
bool is_static;
|
||||
bool strip_debug_symbols;
|
||||
CodeGenBuildType build_type;
|
||||
LLVMTargetMachineRef target_machine;
|
||||
bool is_native_target;
|
||||
Buf *root_source_dir;
|
||||
Buf *root_out_name;
|
||||
|
||||
// The function definitions this module includes. There must be a corresponding
|
||||
// fn_protos entry.
|
||||
ZigList<FnTableEntry *> fn_defs;
|
||||
// The function prototypes this module includes. In the case of external declarations,
|
||||
// there will not be a corresponding fn_defs entry.
|
||||
ZigList<FnTableEntry *> fn_protos;
|
||||
|
||||
OutType out_type;
|
||||
FnTableEntry *cur_fn;
|
||||
LLVMBasicBlockRef cur_basic_block;
|
||||
BlockContext *cur_block_context;
|
||||
bool c_stdint_used;
|
||||
AstNode *root_export_decl;
|
||||
int version_major;
|
||||
int version_minor;
|
||||
int version_patch;
|
||||
bool verbose;
|
||||
ErrColor err_color;
|
||||
ImportTableEntry *root_import;
|
||||
};
|
||||
|
||||
struct LocalVariableTableEntry {
|
||||
Buf name;
|
||||
TypeTableEntry *type;
|
||||
LLVMValueRef value_ref;
|
||||
bool is_const;
|
||||
bool is_ptr; // if true, value_ref is a pointer
|
||||
AstNode *decl_node;
|
||||
LLVMZigDILocalVariable *di_loc_var;
|
||||
int arg_index;
|
||||
};
|
||||
|
||||
struct BlockContext {
|
||||
AstNode *node; // either NodeTypeFnDef or NodeTypeBlock
|
||||
BlockContext *root; // always points to the BlockContext with the NodeTypeFnDef
|
||||
BlockContext *parent; // nullptr when this is the root
|
||||
HashMap<Buf *, LocalVariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
|
||||
LLVMZigDIScope *di_scope;
|
||||
};
|
||||
|
||||
struct TypeNode {
|
||||
TypeTableEntry *entry;
|
||||
};
|
||||
|
||||
struct FnProtoNode {
|
||||
FnTableEntry *fn_table_entry;
|
||||
};
|
||||
|
||||
struct FnDefNode {
|
||||
TypeTableEntry *implicit_return_type;
|
||||
BlockContext *block_context;
|
||||
bool skip;
|
||||
// Required to be a pre-order traversal of the AST. (parents must come before children)
|
||||
ZigList<BlockContext *> all_block_contexts;
|
||||
};
|
||||
|
||||
struct ExprNode {
|
||||
TypeTableEntry *type_entry;
|
||||
// the context in which this expression is evaluated.
|
||||
// for blocks, this points to the containing scope, not the block's own scope for its children.
|
||||
BlockContext *block_context;
|
||||
};
|
||||
|
||||
struct AssignNode {
|
||||
LocalVariableTableEntry *var_entry;
|
||||
};
|
||||
|
||||
struct BlockNode {
|
||||
BlockContext *block_context;
|
||||
};
|
||||
|
||||
struct CodeGenNode {
|
||||
union {
|
||||
TypeNode type_node; // for NodeTypeType
|
||||
FnDefNode fn_def_node; // for NodeTypeFnDef
|
||||
FnProtoNode fn_proto_node; // for NodeTypeFnProto
|
||||
LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel
|
||||
AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign
|
||||
BlockNode block_node; // for NodeTypeBlock
|
||||
} data;
|
||||
ExprNode expr_node; // for all the expression nodes
|
||||
};
|
||||
|
||||
static inline Buf *hack_get_fn_call_name(CodeGen *g, AstNode *node) {
|
||||
// Assume that the expression evaluates to a simple name and return the buf
|
||||
// TODO after type checking works we should be able to remove this hack
|
||||
assert(node->type == NodeTypeSymbol);
|
||||
return &node->data.symbol;
|
||||
}
|
||||
|
||||
void semantic_analyze(CodeGen *g);
|
||||
void add_node_error(CodeGen *g, AstNode *node, Buf *msg);
|
||||
TypeTableEntry *new_type_table_entry();
|
||||
TypeTableEntry *new_type_table_entry(TypeTableEntryId id);
|
||||
TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const);
|
||||
LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name);
|
||||
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
#include "os.hpp"
|
||||
#include "config.h"
|
||||
#include "error.hpp"
|
||||
#include "semantic_info.hpp"
|
||||
#include "analyze.hpp"
|
||||
#include "errmsg.hpp"
|
||||
|
||||
@ -168,6 +167,12 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeArrayAccessExpr);
|
||||
|
||||
zig_panic("TODO gen arary access");
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypePrefixOpExpr);
|
||||
assert(node->data.prefix_op_expr.primary_expr);
|
||||
@ -229,49 +234,55 @@ static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) {
|
||||
return LLVMBuildShl(g->builder, val1, val2, "");
|
||||
case BinOpTypeBitShiftRight:
|
||||
add_debug_source_node(g, node);
|
||||
if (op1_type->is_signed_int) {
|
||||
if (op1_type->id == TypeTableEntryIdInt) {
|
||||
return LLVMBuildAShr(g->builder, val1, val2, "");
|
||||
} else {
|
||||
return LLVMBuildLShr(g->builder, val1, val2, "");
|
||||
}
|
||||
case BinOpTypeAdd:
|
||||
add_debug_source_node(g, node);
|
||||
if (op1_type->is_float) {
|
||||
if (op1_type->id == TypeTableEntryIdFloat) {
|
||||
return LLVMBuildFAdd(g->builder, val1, val2, "");
|
||||
} else {
|
||||
return LLVMBuildNSWAdd(g->builder, val1, val2, "");
|
||||
}
|
||||
case BinOpTypeSub:
|
||||
add_debug_source_node(g, node);
|
||||
if (op1_type->is_float) {
|
||||
if (op1_type->id == TypeTableEntryIdFloat) {
|
||||
return LLVMBuildFSub(g->builder, val1, val2, "");
|
||||
} else {
|
||||
return LLVMBuildNSWSub(g->builder, val1, val2, "");
|
||||
}
|
||||
case BinOpTypeMult:
|
||||
add_debug_source_node(g, node);
|
||||
if (op1_type->is_float) {
|
||||
if (op1_type->id == TypeTableEntryIdFloat) {
|
||||
return LLVMBuildFMul(g->builder, val1, val2, "");
|
||||
} else {
|
||||
return LLVMBuildNSWMul(g->builder, val1, val2, "");
|
||||
}
|
||||
case BinOpTypeDiv:
|
||||
add_debug_source_node(g, node);
|
||||
if (op1_type->is_float) {
|
||||
if (op1_type->id == TypeTableEntryIdFloat) {
|
||||
return LLVMBuildFDiv(g->builder, val1, val2, "");
|
||||
} else if (op1_type->is_signed_int) {
|
||||
return LLVMBuildSDiv(g->builder, val1, val2, "");
|
||||
} else {
|
||||
return LLVMBuildUDiv(g->builder, val1, val2, "");
|
||||
assert(op1_type->id == TypeTableEntryIdInt);
|
||||
if (op1_type->data.integral.is_signed) {
|
||||
return LLVMBuildSDiv(g->builder, val1, val2, "");
|
||||
} else {
|
||||
return LLVMBuildUDiv(g->builder, val1, val2, "");
|
||||
}
|
||||
}
|
||||
case BinOpTypeMod:
|
||||
add_debug_source_node(g, node);
|
||||
if (op1_type->is_float) {
|
||||
if (op1_type->id == TypeTableEntryIdFloat) {
|
||||
return LLVMBuildFRem(g->builder, val1, val2, "");
|
||||
} else if (op1_type->is_signed_int) {
|
||||
return LLVMBuildSRem(g->builder, val1, val2, "");
|
||||
} else {
|
||||
return LLVMBuildURem(g->builder, val1, val2, "");
|
||||
assert(op1_type->id == TypeTableEntryIdInt);
|
||||
if (op1_type->data.integral.is_signed) {
|
||||
return LLVMBuildSRem(g->builder, val1, val2, "");
|
||||
} else {
|
||||
return LLVMBuildURem(g->builder, val1, val2, "");
|
||||
}
|
||||
}
|
||||
case BinOpTypeBoolOr:
|
||||
case BinOpTypeBoolAnd:
|
||||
@ -337,11 +348,13 @@ static LLVMValueRef gen_cmp_expr(CodeGen *g, AstNode *node) {
|
||||
assert(op1_type == op2_type);
|
||||
|
||||
add_debug_source_node(g, node);
|
||||
if (op1_type->is_float) {
|
||||
if (op1_type->id == TypeTableEntryIdFloat) {
|
||||
LLVMRealPredicate pred = cmp_op_to_real_predicate(node->data.bin_op_expr.bin_op);
|
||||
return LLVMBuildFCmp(g->builder, pred, val1, val2, "");
|
||||
} else {
|
||||
LLVMIntPredicate pred = cmp_op_to_int_predicate(node->data.bin_op_expr.bin_op, op1_type->is_signed_int);
|
||||
assert(op1_type->id == TypeTableEntryIdInt);
|
||||
LLVMIntPredicate pred = cmp_op_to_int_predicate(node->data.bin_op_expr.bin_op,
|
||||
op1_type->data.integral.is_signed);
|
||||
return LLVMBuildICmp(g->builder, pred, val1, val2, "");
|
||||
}
|
||||
}
|
||||
@ -596,6 +609,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||
return gen_prefix_op_expr(g, node);
|
||||
case NodeTypeFnCallExpr:
|
||||
return gen_fn_call_expr(g, node);
|
||||
case NodeTypeArrayAccessExpr:
|
||||
return gen_array_access_expr(g, node);
|
||||
case NodeTypeUnreachable:
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildUnreachable(g->builder);
|
||||
@ -865,12 +880,12 @@ static void do_code_gen(CodeGen *g) {
|
||||
static void define_primitive_types(CodeGen *g) {
|
||||
{
|
||||
// if this type is anywhere in the AST, we should never hit codegen.
|
||||
TypeTableEntry *entry = new_type_table_entry();
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInvalid);
|
||||
buf_init_from_str(&entry->name, "(invalid)");
|
||||
g->builtin_types.entry_invalid = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = new_type_table_entry();
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool);
|
||||
entry->type_ref = LLVMInt1Type();
|
||||
buf_init_from_str(&entry->name, "bool");
|
||||
entry->size_in_bits = 1;
|
||||
@ -882,7 +897,7 @@ static void define_primitive_types(CodeGen *g) {
|
||||
g->builtin_types.entry_bool = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = new_type_table_entry();
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
|
||||
entry->type_ref = LLVMInt8Type();
|
||||
buf_init_from_str(&entry->name, "u8");
|
||||
entry->size_in_bits = 8;
|
||||
@ -895,12 +910,12 @@ static void define_primitive_types(CodeGen *g) {
|
||||
}
|
||||
g->builtin_types.entry_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
|
||||
{
|
||||
TypeTableEntry *entry = new_type_table_entry();
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
|
||||
entry->type_ref = LLVMInt32Type();
|
||||
buf_init_from_str(&entry->name, "i32");
|
||||
entry->size_in_bits = 32;
|
||||
entry->align_in_bits = 32;
|
||||
entry->is_signed_int = true;
|
||||
entry->data.integral.is_signed = true;
|
||||
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
|
||||
entry->size_in_bits, entry->align_in_bits,
|
||||
LLVMZigEncoding_DW_ATE_signed());
|
||||
@ -908,12 +923,11 @@ static void define_primitive_types(CodeGen *g) {
|
||||
g->builtin_types.entry_i32 = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = new_type_table_entry();
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
|
||||
entry->type_ref = LLVMFloatType();
|
||||
buf_init_from_str(&entry->name, "f32");
|
||||
entry->size_in_bits = 32;
|
||||
entry->align_in_bits = 32;
|
||||
entry->is_float = true;
|
||||
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
|
||||
entry->size_in_bits, entry->align_in_bits,
|
||||
LLVMZigEncoding_DW_ATE_float());
|
||||
@ -921,7 +935,7 @@ static void define_primitive_types(CodeGen *g) {
|
||||
g->builtin_types.entry_f32 = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = new_type_table_entry();
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid);
|
||||
entry->type_ref = LLVMVoidType();
|
||||
buf_init_from_str(&entry->name, "void");
|
||||
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
|
||||
@ -931,7 +945,7 @@ static void define_primitive_types(CodeGen *g) {
|
||||
g->builtin_types.entry_void = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = new_type_table_entry();
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUnreachable);
|
||||
entry->type_ref = LLVMVoidType();
|
||||
buf_init_from_str(&entry->name, "unreachable");
|
||||
entry->di_type = g->builtin_types.entry_void->di_type;
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "errmsg.hpp"
|
||||
#include "semantic_info.hpp"
|
||||
#include "analyze.hpp"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
@ -70,6 +70,8 @@ const char *node_type_str(NodeType node_type) {
|
||||
return "BinOpExpr";
|
||||
case NodeTypeFnCallExpr:
|
||||
return "FnCallExpr";
|
||||
case NodeTypeArrayAccessExpr:
|
||||
return "ArrayAccessExpr";
|
||||
case NodeTypeExternBlock:
|
||||
return "ExternBlock";
|
||||
case NodeTypeDirective:
|
||||
@ -231,6 +233,11 @@ void ast_print(AstNode *node, int indent) {
|
||||
ast_print(child, indent + 2);
|
||||
}
|
||||
break;
|
||||
case NodeTypeArrayAccessExpr:
|
||||
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||
ast_print(node->data.array_access_expr.array_ref_expr, indent + 2);
|
||||
ast_print(node->data.array_access_expr.subscript, indent + 2);
|
||||
break;
|
||||
case NodeTypeDirective:
|
||||
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||
break;
|
||||
@ -566,10 +573,6 @@ static void ast_parse_param_decl_list(ParseContext *pc, int token_index, int *ne
|
||||
static void ast_parse_fn_call_param_list(ParseContext *pc, int token_index, int *new_token_index,
|
||||
ZigList<AstNode*> *params)
|
||||
{
|
||||
Token *l_paren = &pc->tokens->at(token_index);
|
||||
token_index += 1;
|
||||
ast_expect_token(pc, l_paren, TokenIdLParen);
|
||||
|
||||
Token *token = &pc->tokens->at(token_index);
|
||||
if (token->id == TokenIdRParen) {
|
||||
token_index += 1;
|
||||
@ -680,22 +683,39 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
|
||||
}
|
||||
|
||||
/*
|
||||
FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma)) token(RParen) | PrimaryExpression
|
||||
SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression)
|
||||
FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
|
||||
ArrayAccessExpression : token(LBracket) Expression token(RBracket)
|
||||
*/
|
||||
static AstNode *ast_parse_fn_call_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
AstNode *primary_expr = ast_parse_primary_expr(pc, token_index, mandatory);
|
||||
if (!primary_expr)
|
||||
if (!primary_expr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Token *l_paren = &pc->tokens->at(*token_index);
|
||||
if (l_paren->id != TokenIdLParen)
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
if (token->id == TokenIdLParen) {
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, token);
|
||||
node->data.fn_call_expr.fn_ref_expr = primary_expr;
|
||||
ast_parse_fn_call_param_list(pc, *token_index, token_index, &node->data.fn_call_expr.params);
|
||||
return node;
|
||||
} else if (token->id == TokenIdLBracket) {
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeArrayAccessExpr, token);
|
||||
node->data.array_access_expr.array_ref_expr = primary_expr;
|
||||
node->data.array_access_expr.subscript = ast_parse_expression(pc, token_index, true);
|
||||
|
||||
Token *r_bracket = &pc->tokens->at(*token_index);
|
||||
*token_index += 1;
|
||||
ast_expect_token(pc, r_bracket, TokenIdRBracket);
|
||||
|
||||
return node;
|
||||
} else {
|
||||
return primary_expr;
|
||||
|
||||
AstNode *node = ast_create_node_with_node(pc, NodeTypeFnCallExpr, primary_expr);
|
||||
node->data.fn_call_expr.fn_ref_expr = primary_expr;
|
||||
ast_parse_fn_call_param_list(pc, *token_index, token_index, &node->data.fn_call_expr.params);
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
static PrefixOp tok_to_prefix_op(Token *token) {
|
||||
@ -725,17 +745,17 @@ static PrefixOp ast_parse_prefix_op(ParseContext *pc, int *token_index, bool man
|
||||
}
|
||||
|
||||
/*
|
||||
PrefixOpExpression : PrefixOp FnCallExpression | FnCallExpression
|
||||
PrefixOpExpression : PrefixOp SuffixOpExpression | SuffixOpExpression
|
||||
*/
|
||||
static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
PrefixOp prefix_op = ast_parse_prefix_op(pc, token_index, false);
|
||||
if (prefix_op == PrefixOpInvalid)
|
||||
return ast_parse_fn_call_expr(pc, token_index, mandatory);
|
||||
return ast_parse_suffix_op_expr(pc, token_index, mandatory);
|
||||
|
||||
AstNode *primary_expr = ast_parse_fn_call_expr(pc, token_index, true);
|
||||
AstNode *prefix_op_expr = ast_parse_suffix_op_expr(pc, token_index, true);
|
||||
AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token);
|
||||
node->data.prefix_op_expr.primary_expr = primary_expr;
|
||||
node->data.prefix_op_expr.primary_expr = prefix_op_expr;
|
||||
node->data.prefix_op_expr.prefix_op = prefix_op;
|
||||
|
||||
return node;
|
||||
|
||||
@ -38,6 +38,7 @@ enum NodeType {
|
||||
NodeTypeSymbol,
|
||||
NodeTypePrefixOpExpr,
|
||||
NodeTypeFnCallExpr,
|
||||
NodeTypeArrayAccessExpr,
|
||||
NodeTypeUse,
|
||||
NodeTypeVoid,
|
||||
NodeTypeBoolLiteral,
|
||||
@ -143,6 +144,11 @@ struct AstNodeFnCallExpr {
|
||||
ZigList<AstNode *> params;
|
||||
};
|
||||
|
||||
struct AstNodeArrayAccessExpr {
|
||||
AstNode *array_ref_expr;
|
||||
AstNode *subscript;
|
||||
};
|
||||
|
||||
struct AstNodeExternBlock {
|
||||
ZigList<AstNode *> *directives;
|
||||
ZigList<AstNode *> fn_decls;
|
||||
@ -219,6 +225,7 @@ struct AstNode {
|
||||
AstNodeCastExpr cast_expr;
|
||||
AstNodePrefixOpExpr prefix_op_expr;
|
||||
AstNodeFnCallExpr fn_call_expr;
|
||||
AstNodeArrayAccessExpr array_access_expr;
|
||||
AstNodeUse use;
|
||||
AstNodeIfExpr if_expr;
|
||||
AstNodeLabel label;
|
||||
|
||||
@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Andrew Kelley
|
||||
*
|
||||
* This file is part of zig, which is MIT licensed.
|
||||
* See http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#ifndef ZIG_SEMANTIC_INFO_HPP
|
||||
#define ZIG_SEMANTIC_INFO_HPP
|
||||
|
||||
#include "codegen.hpp"
|
||||
#include "hash_map.hpp"
|
||||
#include "zig_llvm.hpp"
|
||||
#include "errmsg.hpp"
|
||||
|
||||
struct FnTableEntry;
|
||||
struct BlockContext;
|
||||
|
||||
struct TypeTableEntry {
|
||||
LLVMTypeRef type_ref;
|
||||
LLVMZigDIType *di_type;
|
||||
uint64_t size_in_bits;
|
||||
uint64_t align_in_bits;
|
||||
bool is_signed_int;
|
||||
bool is_float;
|
||||
|
||||
TypeTableEntry *pointer_child;
|
||||
bool pointer_is_const;
|
||||
int user_defined_id;
|
||||
Buf name;
|
||||
|
||||
// use these fields to make sure we don't duplicate type table entries for the same type
|
||||
TypeTableEntry *pointer_const_parent;
|
||||
TypeTableEntry *pointer_mut_parent;
|
||||
HashMap<int, TypeTableEntry *, int_hash, int_eq> arrays_by_size;
|
||||
};
|
||||
|
||||
struct ImportTableEntry {
|
||||
AstNode *root;
|
||||
Buf *path; // relative to root_source_dir
|
||||
LLVMZigDIFile *di_file;
|
||||
Buf *source_code;
|
||||
ZigList<int> *line_offsets;
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
struct FnTableEntry {
|
||||
LLVMValueRef fn_value;
|
||||
AstNode *proto_node;
|
||||
AstNode *fn_def_node;
|
||||
bool is_extern;
|
||||
bool internal_linkage;
|
||||
unsigned calling_convention;
|
||||
ImportTableEntry *import_entry;
|
||||
|
||||
// reminder: hash tables must be initialized before use
|
||||
HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
|
||||
};
|
||||
|
||||
struct CodeGen {
|
||||
LLVMModuleRef module;
|
||||
ZigList<ErrorMsg*> errors;
|
||||
LLVMBuilderRef builder;
|
||||
LLVMZigDIBuilder *dbuilder;
|
||||
LLVMZigDICompileUnit *compile_unit;
|
||||
|
||||
// reminder: hash tables must be initialized before use
|
||||
HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
|
||||
HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table;
|
||||
HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
|
||||
HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table;
|
||||
HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
|
||||
|
||||
struct {
|
||||
TypeTableEntry *entry_bool;
|
||||
TypeTableEntry *entry_u8;
|
||||
TypeTableEntry *entry_i32;
|
||||
TypeTableEntry *entry_f32;
|
||||
TypeTableEntry *entry_string_literal;
|
||||
TypeTableEntry *entry_void;
|
||||
TypeTableEntry *entry_unreachable;
|
||||
TypeTableEntry *entry_invalid;
|
||||
} builtin_types;
|
||||
|
||||
LLVMTargetDataRef target_data_ref;
|
||||
unsigned pointer_size_bytes;
|
||||
bool is_static;
|
||||
bool strip_debug_symbols;
|
||||
CodeGenBuildType build_type;
|
||||
LLVMTargetMachineRef target_machine;
|
||||
bool is_native_target;
|
||||
Buf *root_source_dir;
|
||||
Buf *root_out_name;
|
||||
|
||||
// The function definitions this module includes. There must be a corresponding
|
||||
// fn_protos entry.
|
||||
ZigList<FnTableEntry *> fn_defs;
|
||||
// The function prototypes this module includes. In the case of external declarations,
|
||||
// there will not be a corresponding fn_defs entry.
|
||||
ZigList<FnTableEntry *> fn_protos;
|
||||
|
||||
OutType out_type;
|
||||
FnTableEntry *cur_fn;
|
||||
LLVMBasicBlockRef cur_basic_block;
|
||||
BlockContext *cur_block_context;
|
||||
bool c_stdint_used;
|
||||
AstNode *root_export_decl;
|
||||
int version_major;
|
||||
int version_minor;
|
||||
int version_patch;
|
||||
bool verbose;
|
||||
ErrColor err_color;
|
||||
ImportTableEntry *root_import;
|
||||
};
|
||||
|
||||
struct LocalVariableTableEntry {
|
||||
Buf name;
|
||||
TypeTableEntry *type;
|
||||
LLVMValueRef value_ref;
|
||||
bool is_const;
|
||||
bool is_ptr; // if true, value_ref is a pointer
|
||||
AstNode *decl_node;
|
||||
LLVMZigDILocalVariable *di_loc_var;
|
||||
int arg_index;
|
||||
};
|
||||
|
||||
struct BlockContext {
|
||||
AstNode *node; // either NodeTypeFnDef or NodeTypeBlock
|
||||
BlockContext *root; // always points to the BlockContext with the NodeTypeFnDef
|
||||
BlockContext *parent; // nullptr when this is the root
|
||||
HashMap<Buf *, LocalVariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
|
||||
LLVMZigDIScope *di_scope;
|
||||
};
|
||||
|
||||
struct TypeNode {
|
||||
TypeTableEntry *entry;
|
||||
};
|
||||
|
||||
struct FnProtoNode {
|
||||
FnTableEntry *fn_table_entry;
|
||||
};
|
||||
|
||||
struct FnDefNode {
|
||||
TypeTableEntry *implicit_return_type;
|
||||
BlockContext *block_context;
|
||||
bool skip;
|
||||
// Required to be a pre-order traversal of the AST. (parents must come before children)
|
||||
ZigList<BlockContext *> all_block_contexts;
|
||||
};
|
||||
|
||||
struct ExprNode {
|
||||
TypeTableEntry *type_entry;
|
||||
// the context in which this expression is evaluated.
|
||||
// for blocks, this points to the containing scope, not the block's own scope for its children.
|
||||
BlockContext *block_context;
|
||||
};
|
||||
|
||||
struct AssignNode {
|
||||
LocalVariableTableEntry *var_entry;
|
||||
};
|
||||
|
||||
struct BlockNode {
|
||||
BlockContext *block_context;
|
||||
};
|
||||
|
||||
struct CodeGenNode {
|
||||
union {
|
||||
TypeNode type_node; // for NodeTypeType
|
||||
FnDefNode fn_def_node; // for NodeTypeFnDef
|
||||
FnProtoNode fn_proto_node; // for NodeTypeFnProto
|
||||
LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel
|
||||
AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign
|
||||
BlockNode block_node; // for NodeTypeBlock
|
||||
} data;
|
||||
ExprNode expr_node; // for all the expression nodes
|
||||
};
|
||||
|
||||
static inline Buf *hack_get_fn_call_name(CodeGen *g, AstNode *node) {
|
||||
// Assume that the expression evaluates to a simple name and return the buf
|
||||
// TODO after type checking works we should be able to remove this hack
|
||||
assert(node->type == NodeTypeSymbol);
|
||||
return &node->data.symbol;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -392,7 +392,7 @@ fn a() {
|
||||
b(1);
|
||||
}
|
||||
fn b(a: i32, b: i32, c: i32) { }
|
||||
)SOURCE", 1, ".tmp_source.zig:3:5: error: wrong number of arguments. Expected 3, got 1.");
|
||||
)SOURCE", 1, ".tmp_source.zig:3:6: error: wrong number of arguments. Expected 3, got 1.");
|
||||
|
||||
add_compile_fail_case("invalid type", R"SOURCE(
|
||||
fn a() -> bogus {}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user