mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-30 01:44:36 +00:00
Type match statement for tagged unions
This commit is contained in:
@@ -4,20 +4,758 @@
|
||||
|
||||
main :: proc() {
|
||||
Entity :: type union {
|
||||
FROG: struct {
|
||||
Frog: struct {
|
||||
jump_height: f32
|
||||
}
|
||||
HELICOPTER: struct {
|
||||
Helicopter: struct {
|
||||
weight: f32
|
||||
blade_code: int
|
||||
}
|
||||
}
|
||||
|
||||
e: Entity
|
||||
f: Entity = Entity.FROG{1};
|
||||
h: Entity = Entity.HELICOPTER{123, 4};
|
||||
using Entity
|
||||
f: Entity = Frog{137}
|
||||
h: Entity = Helicopter{123, 4}
|
||||
|
||||
match type ^f -> e {
|
||||
case Frog:
|
||||
print_string("Frog!\n")
|
||||
print_f32(e.jump_height); nl()
|
||||
e.jump_height = 69
|
||||
print_f32(e.jump_height); nl()
|
||||
case Helicopter:
|
||||
print_string("Helicopter!\n")
|
||||
e.weight = 1337
|
||||
default:
|
||||
print_string("Unknown!\n")
|
||||
}
|
||||
}
|
||||
|
||||
nl :: proc() { print_nl() }
|
||||
|
||||
/*
|
||||
// Demo 001
|
||||
#load "basic.odin"
|
||||
#load "game.odin"
|
||||
|
||||
main :: proc() {
|
||||
// _ = hellope()
|
||||
// procedures()
|
||||
// variables()
|
||||
// constants()
|
||||
// types()
|
||||
// data_control()
|
||||
// using_fields()
|
||||
|
||||
run_game()
|
||||
}
|
||||
|
||||
|
||||
hellope :: proc() -> int {
|
||||
print_string("Hellope, 世界\n")
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
// Line comment
|
||||
/*
|
||||
Block Comment
|
||||
*/
|
||||
/*
|
||||
Nested /*
|
||||
Block /*
|
||||
Comment
|
||||
*/
|
||||
*/
|
||||
*/
|
||||
|
||||
apple, banana, carrot: bool
|
||||
box, carboard: bool = true, false
|
||||
// hellope_value: int = hellope() // The procedure is ran just before `main`
|
||||
|
||||
variables :: proc() {
|
||||
i: int // initialized with zero value
|
||||
j: int = 1
|
||||
x, y: int = 1, 2
|
||||
|
||||
// Type inference
|
||||
apple, banana, 世界 := true, 123, "world"
|
||||
|
||||
|
||||
// Basic Types of the Language
|
||||
//
|
||||
// bool
|
||||
//
|
||||
// i8 i16 i32 i64 i128
|
||||
// u8 u16 u32 u64 u128
|
||||
//
|
||||
// f32 f64
|
||||
//
|
||||
// int uint (size_of(int) == size_of(uint) == size_of(rawptr))
|
||||
//
|
||||
// rawptr (equivalent to void * in C/C++)
|
||||
//
|
||||
// string
|
||||
//
|
||||
// byte - alias for u8
|
||||
// rune - alias for i32 // Unicode Codepoint
|
||||
//
|
||||
// "untyped" types can implicitly convert to any of the "typed" types
|
||||
// Default Type
|
||||
// untyped bool - bool
|
||||
// untyped integer - int
|
||||
// untyped float - f64
|
||||
// untyped pointer - rawptr
|
||||
// untyped string - string
|
||||
// untyped rune - rune/i32
|
||||
|
||||
|
||||
// Zero values
|
||||
zero_numeric := 0
|
||||
zero_boolean := false
|
||||
zero_pointer := null
|
||||
zero_string1 := "" // Escaped string
|
||||
zero_string2 := `` // Raw string
|
||||
// Compound types have a different kind of zero value
|
||||
|
||||
// Unary operators
|
||||
// +a
|
||||
// -a
|
||||
// ~a
|
||||
// !a
|
||||
|
||||
// Binary operators
|
||||
// a + b add
|
||||
// a - b sub
|
||||
// a ~ b xor
|
||||
// a | b or
|
||||
|
||||
// a * b mul
|
||||
// a / b quo
|
||||
// a % b mod
|
||||
// a & b and
|
||||
// a &~ b bitclear == a & (~b)
|
||||
// a << b shl
|
||||
// a >> b shr
|
||||
|
||||
// a as Type // Type cast
|
||||
// a transmute Type // Bit cast
|
||||
|
||||
// a == b eq
|
||||
// a != b ne
|
||||
// a < b lt
|
||||
// a > b gt
|
||||
// a <= b le
|
||||
// a >= b ge
|
||||
|
||||
}
|
||||
|
||||
procedures :: proc() {
|
||||
add :: proc(x: int, y: int) -> int {
|
||||
return x + y
|
||||
}
|
||||
print_int(add(3, 4)) // 7
|
||||
print_nl()
|
||||
|
||||
add_v2 :: proc(x, y: int) -> int {
|
||||
return x + y
|
||||
}
|
||||
|
||||
fibonacci :: proc(n: int) -> int {
|
||||
if n < 2 {
|
||||
return n
|
||||
}
|
||||
return fibonacci(n-1) + fibonacci(n-2)
|
||||
}
|
||||
print_int(fibonacci(12)); nl()
|
||||
|
||||
|
||||
swap_strings :: proc(x, y: string) -> (string, string) {
|
||||
return y, x
|
||||
}
|
||||
a, b := swap_strings("Hellope\n", "World\n")
|
||||
print_string(a)
|
||||
print_string(b)
|
||||
|
||||
a, b = b, a // Quirk of grammar the of multiple assignments
|
||||
// Swap variables
|
||||
print_string(a)
|
||||
print_string(b)
|
||||
|
||||
// Not a hint like C/C++, it's mandatory (unless it cannot do it but it will warn)
|
||||
proc1 :: proc(a, b: int) #inline {
|
||||
print_int(a + b)
|
||||
}
|
||||
proc2 :: proc(a, b: int) #no_inline {
|
||||
print_int(a + b)
|
||||
}
|
||||
|
||||
print_int(3 ''add 4) // Infix style
|
||||
print_nl()
|
||||
print_int(12 'fibonacci) // Postfix style
|
||||
print_nl()
|
||||
}
|
||||
|
||||
|
||||
TAU :: 6.28318530718
|
||||
|
||||
constants :: proc() {
|
||||
TAU :: 6.28318530718 // untyped float
|
||||
WORLD_JAPANESE :: "世界" // untyped string
|
||||
|
||||
TAU_32 : f32 : 6.28318530718
|
||||
TAU_AS_32 :: 6.28318530718 as f32
|
||||
|
||||
PI :: TAU / 2
|
||||
|
||||
CLOSE_TO_PI :: 3
|
||||
|
||||
DIFF :: (PI - CLOSE_TO_PI) / PI // Evaluated at compile time
|
||||
|
||||
a := TAU // the constant's value becomes typed as f32
|
||||
b := CLOSE_TO_PI // the constant's value becomes typed as int
|
||||
c := DIFF
|
||||
}
|
||||
|
||||
nl :: proc() { print_nl() }
|
||||
|
||||
types :: proc() {
|
||||
|
||||
x: int = 123
|
||||
y := x // y: int = x
|
||||
// z: f32 = x // invalid
|
||||
z: f32 = x as f32
|
||||
|
||||
|
||||
ptr_z := ^z // Pascal notation
|
||||
ptr_z^ = 123 // Derefence Notation
|
||||
w: f32 = ptr_z^ // 123
|
||||
print_f32(z); nl()
|
||||
|
||||
// ^z - pointer to z
|
||||
// z^ - z from pointer
|
||||
|
||||
// Implicit conversion to and from rawptr
|
||||
r_ptr: rawptr = ptr_z
|
||||
ptr_z = r_ptr
|
||||
|
||||
|
||||
|
||||
|
||||
f32_array: [12]f32 // Array of 12 f32
|
||||
f32_array[0] = 2
|
||||
f32_array[1] = 3
|
||||
// f32_array[-1] = 2 // Error - compile time check
|
||||
// f32_array[13] = 2 // Error - compile time check
|
||||
f32_array_len := len(f32_array) // builtin procedure
|
||||
f32_array_cap := cap(f32_array) // == len(f32_array)
|
||||
|
||||
|
||||
mda: [2][3][4]int // Column-major
|
||||
// mda[x][y][z]
|
||||
|
||||
|
||||
|
||||
api: [2]^f32
|
||||
papi: ^[2]^f32
|
||||
|
||||
|
||||
|
||||
|
||||
f32_slice: []f32 // Slice / Array reference
|
||||
f32_slice = f32_array[0:5]
|
||||
f32_slice = f32_array[:5]
|
||||
f32_slice = f32_array[:] // f32_array[0:len(f32_array)-1]
|
||||
|
||||
f32_slice = f32_array[1:5:7] // low:1, high:5, max:7
|
||||
// len: 5-1 == 4
|
||||
// cap: 7-1 == 6
|
||||
|
||||
|
||||
|
||||
append_success := append(^f32_slice, 1)
|
||||
_ = append(^f32_slice, 2)
|
||||
|
||||
_ = copy(f32_array[0:2], f32_array[2:4]) // You can use memcpy/memmove if you want
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
s := "Hellope World"
|
||||
sub_string: string = s[5:10]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
v0: {4}f32 // Vector of 4 f32
|
||||
v0[0] = 1
|
||||
v0[1] = 3
|
||||
v0[2] = 6
|
||||
v0[3] = 10
|
||||
|
||||
v1 := v0 + v0 // Simd Arithmetic
|
||||
v1 = v1 - v0
|
||||
v1 *= v0 // i.e. hadamard product
|
||||
v1 /= v0
|
||||
|
||||
// builtin procedure
|
||||
v2 := swizzle(v0, 3, 2, 1, 0) // {10, 6, 3, 1}
|
||||
|
||||
v3: {4}bool = v0 == v2
|
||||
// LLVM rant?
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Vec4 :: type {4}f32
|
||||
Array3Int :: type [3]int
|
||||
|
||||
Vec3 :: type struct {
|
||||
x, y, z: f32
|
||||
}
|
||||
|
||||
BinaryNode :: type struct {
|
||||
left, right: ^BinaryNode // same format as procedure argument
|
||||
data: rawptr
|
||||
}
|
||||
|
||||
AddProc :: type proc(a, b: int) -> int
|
||||
|
||||
Packed :: type struct #packed {
|
||||
a: u8
|
||||
b: u16
|
||||
c: u32
|
||||
}
|
||||
assert(size_of(Packed) == 7) // builtin procedure
|
||||
{
|
||||
a, b: ^BinaryNode
|
||||
a = alloc(size_of(BinaryNode)) as ^BinaryNode
|
||||
b = alloc(size_of(BinaryNode)) as ^BinaryNode
|
||||
|
||||
c := BinaryNode{a, b, null}
|
||||
c.left^.data = null
|
||||
c.left.data = null // No need to deference
|
||||
|
||||
dealloc(a)
|
||||
dealloc(b)
|
||||
}
|
||||
|
||||
{
|
||||
MyInt :: type int
|
||||
x: int = 1
|
||||
y: MyInt = 2
|
||||
// z := x + y // Failure - types cannot implicit convert*
|
||||
z := x as MyInt + y // Type cast using `as`
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
// From: Quake III Arena
|
||||
Q_rsqrt :: proc(number: f32) -> f32 {
|
||||
i: i32
|
||||
x2, y: f32
|
||||
THREE_HALFS :: 1.5
|
||||
|
||||
x2 = number * 0.5
|
||||
y = number
|
||||
i = (^y as ^i32)^ // evil floating point bit level hacking
|
||||
i = 0x5f3759df - i>>1 // what the fuck?
|
||||
y = (^i as ^f32)^
|
||||
y = y * (THREE_HALFS - (x2 * y *y)) // 1st iteration
|
||||
// y = y * (THREE_HALFS - (x2 * y *y)) // 2nd iteration, this can be removed
|
||||
return y
|
||||
}
|
||||
|
||||
Q_rsqrt_v2 :: proc(number: f32) -> f32 {
|
||||
THREE_HALFS :: 1.5
|
||||
|
||||
x2 := number * 0.5
|
||||
y := number
|
||||
i := y transmute i32 // evil floating point bit level hacking
|
||||
i = 0x5f3759df - i>>1 // what the fuck?
|
||||
y = i transmute f32
|
||||
y = y * (THREE_HALFS - (x2 * y *y)) // 1st iteration
|
||||
// y = y * (THREE_HALFS - (x2 * y *y)) // 2nd iteration, this can be removed
|
||||
return y
|
||||
}
|
||||
|
||||
// NOTE(bill): transmute only works if the size of the types are equal
|
||||
|
||||
/*
|
||||
// in C
|
||||
union {
|
||||
i32 i
|
||||
f32 y
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
{ // Enumeration
|
||||
Thing :: type enum {
|
||||
APPLE,
|
||||
FROG,
|
||||
TREE,
|
||||
TOMB,
|
||||
}
|
||||
a := Thing.APPLE
|
||||
|
||||
Sized :: type enum u64 {
|
||||
APPLE,
|
||||
FROG,
|
||||
TREE,
|
||||
TOMB,
|
||||
}
|
||||
assert(size_of(Sized) == size_of(u64))
|
||||
|
||||
Certain :: type enum {
|
||||
APPLE = 3,
|
||||
FROG,
|
||||
TREE = 7,
|
||||
TOMB,
|
||||
}
|
||||
assert(Certain.TOMB == 8)
|
||||
}
|
||||
|
||||
{ // Untagged union
|
||||
BitHack :: type raw_union {
|
||||
i: i32
|
||||
f: f32
|
||||
}
|
||||
b: BitHack
|
||||
b.f = 123
|
||||
print_int(b.i as int); print_nl()
|
||||
|
||||
|
||||
|
||||
// Manually tagged union
|
||||
|
||||
EntityKind :: type enum {
|
||||
Invalid,
|
||||
Constant,
|
||||
Variable,
|
||||
TypeName,
|
||||
Procedure,
|
||||
Builtin,
|
||||
Count,
|
||||
}
|
||||
|
||||
Entity :: type struct {
|
||||
kind: EntityKind
|
||||
guid: u64
|
||||
|
||||
// Other data
|
||||
|
||||
/*using*/
|
||||
data: union {
|
||||
constant: struct{}
|
||||
variable: struct{
|
||||
visited, is_field, used, anonymous: bool
|
||||
}
|
||||
procedure: struct { used: bool }
|
||||
buitlin: struct { id: i32 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): Tagged unions are not added yet but are planned
|
||||
}
|
||||
|
||||
|
||||
|
||||
{ // Compound Literals
|
||||
a := [3]int{1, 2, 3}
|
||||
b := [3]int{}
|
||||
c := [..]int{1, 2, 3}
|
||||
|
||||
d := []int{1, 2, 3} // slice
|
||||
|
||||
e := {4}f32{1, 2, 3, 4}
|
||||
f := {4}f32{1} // broadcasts to all
|
||||
// g := {4}f32{1, 2} // require either 1 or 4 elements
|
||||
|
||||
Vec2 :: type {2}f32
|
||||
|
||||
h := Vec2{1, 2}
|
||||
|
||||
i := Vec2{5} * h // For strong type safety
|
||||
// FORENOTE: 5 * h was originally allowed but it was an edge case in the
|
||||
// compiler I didn't think it was enough to justify have it it.
|
||||
|
||||
print_f32(i[0]); print_rune(#rune ",")
|
||||
print_f32(i[1]); print_nl()
|
||||
}
|
||||
|
||||
|
||||
|
||||
{ // First class procedures
|
||||
|
||||
do_thing :: proc(p: proc(a, b: int) -> int) {
|
||||
print_int(p(3, 4)); nl()
|
||||
}
|
||||
|
||||
add :: proc(a, b: int) -> int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
|
||||
add_lambda := proc(a, b: int) -> int {
|
||||
return a - b
|
||||
} // note semicolon
|
||||
|
||||
do_thing(add)
|
||||
do_thing(add_lambda)
|
||||
do_thing(proc(a, b: int) -> int { // Anonymous
|
||||
return a * b
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
{ // strings and runes
|
||||
escaped := "Hellope World\n"
|
||||
raw := `Hellope World\n`
|
||||
print_string(escaped)
|
||||
print_string(raw); nl()
|
||||
|
||||
// Crap shader example
|
||||
shader_string :=
|
||||
`#version 410
|
||||
|
||||
layout (location = 0) in vec3 a_position
|
||||
layout (location = 1) in vec3 a_normal;
|
||||
layout (location = 2) in vec2 a_tex_coord;
|
||||
|
||||
out vec3 v_position;
|
||||
out vec3 v_normal;
|
||||
out vec2 v_tex_coord;
|
||||
|
||||
uniform mat4 u_model_view;
|
||||
uniform mat3 u_normal;
|
||||
uniform mat4 u_proj;
|
||||
uniform mat4 u_mvp;
|
||||
|
||||
void main() {
|
||||
v_tex_coord = a_tex_coord;
|
||||
v_normal = normalize(u_normal * a_normal);
|
||||
v_position = vec3(u_model_view * vec4(a_position, 1.0));
|
||||
|
||||
gl_Position = u_mvp * vec4(a_position, 1.0);
|
||||
}`;
|
||||
|
||||
|
||||
hearts1 := #rune "💕";
|
||||
hearts2 := #rune "\U0001f495"; // 32 bit
|
||||
hearts3 := #rune "\xf0\x9f\x92\x95";
|
||||
|
||||
㐒 := #rune "㐒";
|
||||
㐒16 := #rune "\u4db5"; // 16 bit but will be `rune`
|
||||
// String ideas "nicked" from Go, so far. I think I might change how some of it works later.
|
||||
}
|
||||
|
||||
|
||||
{ // size, align, offset
|
||||
Thing :: type struct {
|
||||
a: u8;
|
||||
b: u16;
|
||||
c, d, e: u32;
|
||||
}
|
||||
|
||||
s := size_of(Thing);
|
||||
a := align_of(Thing);
|
||||
o := offset_of(Thing, b);
|
||||
|
||||
t: Thing;
|
||||
|
||||
sv := size_of_val(t);
|
||||
av := align_of_val(t);
|
||||
ov := offset_of_val(t.b);
|
||||
}
|
||||
}
|
||||
|
||||
data_control :: proc() {
|
||||
sum := 0
|
||||
for i := 0; i < 12; i++ {
|
||||
sum += 1
|
||||
}
|
||||
print_string("sum = "); print_int(sum); nl()
|
||||
|
||||
sum = 1
|
||||
for ; sum < 1000000; {
|
||||
sum += sum
|
||||
}
|
||||
print_string("sum = "); print_int(sum); nl()
|
||||
|
||||
sum = 1
|
||||
for sum < 1000000 {
|
||||
sum += sum
|
||||
}
|
||||
print_string("sum = "); print_int(sum); nl()
|
||||
|
||||
// loop
|
||||
// for { } == for true {}
|
||||
|
||||
// Question: Should I separate all these concepts and rename it?
|
||||
//
|
||||
// range - iterable
|
||||
// for - c style
|
||||
// while
|
||||
// loop - while true
|
||||
|
||||
// Notes:
|
||||
// conditions _must_ a boolean expression
|
||||
// i++ and i-- are statements, not expressions
|
||||
|
||||
|
||||
x := 2
|
||||
if x < 3 {
|
||||
print_string("x < 2\n")
|
||||
}
|
||||
|
||||
// Unified initializer syntax - same as for statements
|
||||
if x := 2; x < 3 {
|
||||
print_string("x < 2\n")
|
||||
}
|
||||
|
||||
if x := 4; x < 3 {
|
||||
print_string("Never called\n")
|
||||
} else {
|
||||
print_string("This is called\n")
|
||||
}
|
||||
|
||||
{ // String comparison
|
||||
a := "Hellope"
|
||||
b := "World"
|
||||
if a < b {
|
||||
print_string("a < b\n")
|
||||
}
|
||||
if a != b {
|
||||
print_string("a != b\n")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
{ // Defer statement
|
||||
defer print_string("日本語\n")
|
||||
print_string("Japanese\n")
|
||||
}
|
||||
|
||||
{
|
||||
defer print_string("1\n")
|
||||
defer print_string("2\n")
|
||||
defer print_string("3\n")
|
||||
}
|
||||
|
||||
{
|
||||
prev_allocator := context.allocator
|
||||
context.allocator = __default_allocator()
|
||||
defer context.allocator = prev_allocator
|
||||
|
||||
File :: type struct { filename: string }
|
||||
FileError :: type int;
|
||||
open_file :: proc(filename: string) -> (File, FileError) {
|
||||
return File{}, 0
|
||||
}
|
||||
close_file :: proc(f: ^File) {}
|
||||
f, err := open_file("Test")
|
||||
if err != 0 {
|
||||
// handle error
|
||||
}
|
||||
defer close_file(^f)
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
blah := new_slice(int, 100)
|
||||
defer {
|
||||
defer print_string("!")
|
||||
defer print_string("dealloc")
|
||||
delete(blah)
|
||||
}
|
||||
|
||||
if i == 3 {
|
||||
// defers called
|
||||
continue
|
||||
}
|
||||
|
||||
if i == 5 {
|
||||
// defers called
|
||||
return // End of procedure
|
||||
}
|
||||
|
||||
if i == 8 {
|
||||
// defers called
|
||||
break // never happens
|
||||
}
|
||||
}
|
||||
|
||||
defer print_string("It'll never happen, mate 1")
|
||||
print_string("It'll never happen, mate 2")
|
||||
print_string("It'll never happen, mate 3")
|
||||
}
|
||||
|
||||
|
||||
using_fields :: proc() {
|
||||
{ // Everyday stuff
|
||||
Vec3 :: type struct { x, y, z: f32; }
|
||||
|
||||
Entity :: type struct {
|
||||
name: string;
|
||||
using pos: Vec3;
|
||||
vel: Vec3;
|
||||
}
|
||||
t: Entity;
|
||||
t.y = 456;
|
||||
print_f32(t.y); print_nl();
|
||||
print_f32(t.pos.y); print_nl();
|
||||
print_f32(t.vel.y); print_nl();
|
||||
|
||||
|
||||
Frog :: type struct { // Subtype (kind of)
|
||||
using entity: Entity;
|
||||
colour: u32;
|
||||
jump_height: f32;
|
||||
}
|
||||
|
||||
f: Frog;
|
||||
f.y = 1337;
|
||||
print_f32(f.y); print_nl();
|
||||
print_f32(f.pos.y); print_nl();
|
||||
print_f32(f.vel.y); print_nl();
|
||||
|
||||
|
||||
Buffalo :: type struct {
|
||||
using entity: Entity;
|
||||
speed: f32;
|
||||
noise_level: f32;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{ // Crazy Shit
|
||||
Vec2 :: type raw_union {
|
||||
using _xy: struct {x, y: f32};
|
||||
e: [2]f32;
|
||||
v: {2}f32;
|
||||
}
|
||||
|
||||
Entity :: type struct {
|
||||
using pos: ^Vec2;
|
||||
name: string;
|
||||
}
|
||||
t: Entity;
|
||||
t.pos = alloc(size_of(Vec2)) as ^Vec2; // TODO(bill): make an alloc type? i.e. new(Type)?
|
||||
t.x = 123;
|
||||
print_f32(t._xy.x); print_nl();
|
||||
print_f32(t.pos.x); print_nl();
|
||||
print_f32(t.pos._xy.x); print_nl();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -197,12 +197,13 @@ struct CheckerContext {
|
||||
|
||||
// NOTE(bill): Symbol tables
|
||||
struct CheckerInfo {
|
||||
Map<TypeAndValue> types; // Key: AstNode * | Expression -> Type (and value)
|
||||
Map<Entity *> definitions; // Key: AstNode * | Identifier -> Entity
|
||||
Map<Entity *> uses; // Key: AstNode * | Identifier -> Entity
|
||||
Map<Scope *> scopes; // Key: AstNode * | Node -> Scope
|
||||
Map<ExpressionInfo> untyped; // Key: AstNode * | Expression -> ExpressionInfo
|
||||
Map<DeclInfo *> entities; // Key: Entity *
|
||||
Map<TypeAndValue> types; // Key: AstNode * | Expression -> Type (and value)
|
||||
Map<Entity *> definitions; // Key: AstNode * | Identifier -> Entity
|
||||
Map<Entity *> uses; // Key: AstNode * | Identifier -> Entity
|
||||
Map<Scope *> scopes; // Key: AstNode * | Node -> Scope
|
||||
Map<ExpressionInfo> untyped; // Key: AstNode * | Expression -> ExpressionInfo
|
||||
Map<DeclInfo *> entities; // Key: Entity *
|
||||
Map<Entity *> foreign_procs; // Key: String
|
||||
};
|
||||
|
||||
struct Checker {
|
||||
@@ -424,12 +425,13 @@ void init_universal_scope(void) {
|
||||
|
||||
void init_checker_info(CheckerInfo *i) {
|
||||
gbAllocator a = gb_heap_allocator();
|
||||
map_init(&i->types, a);
|
||||
map_init(&i->definitions, a);
|
||||
map_init(&i->uses, a);
|
||||
map_init(&i->scopes, a);
|
||||
map_init(&i->entities, a);
|
||||
map_init(&i->untyped, a);
|
||||
map_init(&i->types, a);
|
||||
map_init(&i->definitions, a);
|
||||
map_init(&i->uses, a);
|
||||
map_init(&i->scopes, a);
|
||||
map_init(&i->entities, a);
|
||||
map_init(&i->untyped, a);
|
||||
map_init(&i->foreign_procs, a);
|
||||
|
||||
}
|
||||
|
||||
@@ -440,6 +442,7 @@ void destroy_checker_info(CheckerInfo *i) {
|
||||
map_destroy(&i->scopes);
|
||||
map_destroy(&i->entities);
|
||||
map_destroy(&i->untyped);
|
||||
map_destroy(&i->foreign_procs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -296,12 +296,16 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
|
||||
ast_node(vd, VarDecl, decl);
|
||||
if (vd->kind != Declaration_Mutable)
|
||||
continue;
|
||||
Type *type = check_type(c, vd->type, NULL, cycle_checker);
|
||||
Type *base_type = check_type(c, vd->type, NULL, cycle_checker);
|
||||
|
||||
for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
|
||||
Token name_token = name->Ident;
|
||||
|
||||
Type *type = make_type_named(c->allocator, name_token.string, base_type, NULL);
|
||||
Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, type);
|
||||
type->Named.type_name = e;
|
||||
add_entity(c, c->context.scope, name, e);
|
||||
|
||||
HashKey key = hash_string(name_token.string);
|
||||
if (map_get(&entity_map, key) != NULL) {
|
||||
// TODO(bill): Scope checking already checks the declaration
|
||||
@@ -309,7 +313,6 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
|
||||
} else {
|
||||
map_set(&entity_map, key, e);
|
||||
fields[field_index++] = e;
|
||||
add_entity(c, c->context.scope, name, e);
|
||||
}
|
||||
add_entity_use(&c->info, name, e);
|
||||
}
|
||||
@@ -3154,7 +3157,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
|
||||
goto error;
|
||||
}
|
||||
|
||||
check_index_value(c, ie->index, max_count, NULL);
|
||||
i64 index = 0;
|
||||
b32 ok = check_index_value(c, ie->index, max_count, &index);
|
||||
|
||||
case_end;
|
||||
|
||||
|
||||
|
||||
@@ -127,6 +127,21 @@ b32 check_is_terminating(AstNode *node) {
|
||||
}
|
||||
return has_default;
|
||||
case_end;
|
||||
|
||||
case_ast_node(ms, TypeMatchStmt, node);
|
||||
b32 has_default = false;
|
||||
for (AstNode *clause = ms->body->BlockStmt.list; clause != NULL; clause = clause->next) {
|
||||
ast_node(cc, CaseClause, clause);
|
||||
if (cc->list == NULL) {
|
||||
has_default = true;
|
||||
}
|
||||
if (!check_is_terminating_list(cc->stmts) ||
|
||||
check_has_break_list(cc->stmts, true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return has_default;
|
||||
case_end;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -475,6 +490,31 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
|
||||
}
|
||||
}
|
||||
|
||||
if (is_foreign) {
|
||||
auto *fp = &c->info.foreign_procs;
|
||||
auto *proc_decl = &d->proc_decl->ProcDecl;
|
||||
String name = proc_decl->name->Ident.string;
|
||||
if (proc_decl->foreign_name.len > 0) {
|
||||
name = proc_decl->foreign_name;
|
||||
}
|
||||
HashKey key = hash_string(name);
|
||||
auto *found = map_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
Type *this_type = get_base_type(e->type);
|
||||
Type *other_type = get_base_type(f->type);
|
||||
if (!are_types_identical(this_type, other_type)) {
|
||||
error(&c->error_collector, ast_node_token(d->proc_decl),
|
||||
"Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
}
|
||||
} else {
|
||||
map_set(fp, key, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) {
|
||||
@@ -1018,8 +1058,122 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
check_close_scope(c);
|
||||
i++;
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(ms, TypeMatchStmt, node);
|
||||
Operand x = {};
|
||||
|
||||
mod_flags |= Stmt_BreakAllowed;
|
||||
check_open_scope(c, node);
|
||||
defer (check_close_scope(c));
|
||||
|
||||
|
||||
check_expr(c, &x, ms->tag);
|
||||
check_assignment(c, &x, NULL, make_string("type match expression"));
|
||||
if (!is_type_pointer(x.type) || !is_type_union(type_deref(x.type))) {
|
||||
gbString str = type_to_string(x.type);
|
||||
defer (gb_string_free(str));
|
||||
error(&c->error_collector, ast_node_token(x.expr),
|
||||
"Expected a pointer to a union for this type match expression, got `%s`", str);
|
||||
break;
|
||||
}
|
||||
Type *base_union = get_base_type(type_deref(x.type));
|
||||
|
||||
|
||||
// NOTE(bill): Check for multiple defaults
|
||||
AstNode *first_default = NULL;
|
||||
ast_node(bs, BlockStmt, ms->body);
|
||||
for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) {
|
||||
AstNode *default_stmt = NULL;
|
||||
if (stmt->kind == AstNode_CaseClause) {
|
||||
ast_node(c, CaseClause, stmt);
|
||||
if (c->list_count == 0) {
|
||||
default_stmt = stmt;
|
||||
}
|
||||
} else {
|
||||
error(&c->error_collector, ast_node_token(stmt), "Invalid AST - expected case clause");
|
||||
}
|
||||
|
||||
if (default_stmt != NULL) {
|
||||
if (first_default != NULL) {
|
||||
TokenPos pos = ast_node_token(first_default).pos;
|
||||
error(&c->error_collector, ast_node_token(stmt),
|
||||
"multiple `default` clauses\n"
|
||||
"\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
|
||||
} else {
|
||||
first_default = default_stmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ms->var->kind != AstNode_Ident) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Map<b32> seen = {};
|
||||
map_init(&seen, gb_heap_allocator());
|
||||
defer (map_destroy(&seen));
|
||||
|
||||
|
||||
|
||||
for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) {
|
||||
if (stmt->kind != AstNode_CaseClause) {
|
||||
// NOTE(bill): error handled by above multiple default checker
|
||||
continue;
|
||||
}
|
||||
ast_node(cc, CaseClause, stmt);
|
||||
|
||||
AstNode *type_expr = cc->list;
|
||||
Type *tag_type = NULL;
|
||||
if (type_expr != NULL) { // Otherwise it's a default expression
|
||||
Operand y = {};
|
||||
check_expr_or_type(c, &y, type_expr);
|
||||
b32 tag_type_found = false;
|
||||
for (isize i = 0; i < base_union->Record.field_count; i++) {
|
||||
Entity *f = base_union->Record.fields[i];
|
||||
if (are_types_identical(f->type, y.type)) {
|
||||
tag_type_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!tag_type_found) {
|
||||
gbString type_str = type_to_string(y.type);
|
||||
defer (gb_string_free(type_str));
|
||||
error(&c->error_collector, ast_node_token(y.expr),
|
||||
"Unknown tag type, got `%s`", type_str);
|
||||
continue;
|
||||
}
|
||||
tag_type = y.type;
|
||||
|
||||
HashKey key = hash_pointer(y.type);
|
||||
auto *found = map_get(&seen, key);
|
||||
if (found) {
|
||||
TokenPos pos = cc->token.pos;
|
||||
gbString expr_str = expr_to_string(y.expr);
|
||||
error(&c->error_collector,
|
||||
ast_node_token(y.expr),
|
||||
"Duplicate type case `%s`\n"
|
||||
"\tprevious type case at %.*s(%td:%td)",
|
||||
expr_str,
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
gb_string_free(expr_str);
|
||||
break;
|
||||
}
|
||||
map_set(&seen, key, cast(b32)true);
|
||||
|
||||
}
|
||||
|
||||
check_open_scope(c, stmt);
|
||||
if (tag_type != NULL) {
|
||||
// NOTE(bill): Dummy type
|
||||
Type *tag_ptr_type = make_type_pointer(c->allocator, tag_type);
|
||||
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tag_ptr_type);
|
||||
add_entity(c, c->context.scope, ms->var, tag_var);
|
||||
}
|
||||
check_stmt_list(c, cc->stmts, cc->stmt_count, mod_flags);
|
||||
check_close_scope(c);
|
||||
}
|
||||
case_end;
|
||||
|
||||
|
||||
@@ -1096,27 +1250,27 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
} else if (is_type_struct(t)) {
|
||||
Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
|
||||
if (found != NULL) {
|
||||
gb_for_array(i, (*found)->elements.entries) {
|
||||
Entity *f = (*found)->elements.entries[i].value;
|
||||
Entity *found = scope_insert_entity(c->context.scope, f);
|
||||
if (found != NULL) {
|
||||
error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
return;
|
||||
}
|
||||
f->using_parent = e;
|
||||
GB_ASSERT(found != NULL);
|
||||
gb_for_array(i, (*found)->elements.entries) {
|
||||
Entity *f = (*found)->elements.entries[i].value;
|
||||
Entity *found = scope_insert_entity(c->context.scope, f);
|
||||
if (found != NULL) {
|
||||
error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
for (isize i = 0; i < t->Record.other_field_count; i++) {
|
||||
// TODO(bill): using field types too
|
||||
Entity *f = t->Record.other_fields[i];
|
||||
Entity *found = scope_insert_entity(c->context.scope, f);
|
||||
if (found != NULL) {
|
||||
error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
return;
|
||||
}
|
||||
f->using_parent = e;
|
||||
f->using_parent = e;
|
||||
}
|
||||
} else if (is_type_union(t)) {
|
||||
Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
|
||||
GB_ASSERT(found != NULL);
|
||||
gb_for_array(i, (*found)->elements.entries) {
|
||||
Entity *f = (*found)->elements.entries[i].value;
|
||||
Entity *found = scope_insert_entity(c->context.scope, f);
|
||||
if (found != NULL) {
|
||||
error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
return;
|
||||
}
|
||||
f->using_parent = e;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -31,6 +31,27 @@ b32 ssa_gen_init(ssaGen *s, Checker *c) {
|
||||
if (err != gbFileError_None)
|
||||
return false;
|
||||
|
||||
#if 0
|
||||
Map<i32> type_map; // Key: Type *
|
||||
map_init(&type_map, gb_heap_allocator());
|
||||
i32 index = 0;
|
||||
gb_for_array(i, c->info.types.entries) {
|
||||
TypeAndValue tav = c->info.types.entries[i].value;
|
||||
Type *type = tav.type;
|
||||
HashKey key = hash_pointer(type);
|
||||
auto found = map_get(&type_map, key);
|
||||
if (!found) {
|
||||
map_set(&type_map, key, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
gb_for_array(i, type_map.entries) {
|
||||
auto *e = &type_map.entries[i];
|
||||
Type *t = cast(Type *)cast(uintptr)e->key.key;
|
||||
gb_printf("%s\n", type_to_string(t));
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -112,7 +133,10 @@ void ssa_gen_code(ssaGen *s) {
|
||||
p->Proc.tags = pd->tags;
|
||||
|
||||
map_set(&m->values, hash_pointer(e), p);
|
||||
map_set(&m->members, hash_string(name), p);
|
||||
HashKey hash_name = hash_string(name);
|
||||
if (map_get(&m->members, hash_name) == NULL) {
|
||||
map_set(&m->members, hash_name, p);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,22 +319,22 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
|
||||
ssa_fprintf(f, "call void @" SSA_STARTUP_RUNTIME_PROC_NAME "()\n");
|
||||
} break;
|
||||
|
||||
case ssaInstr_Comment:
|
||||
ssa_fprintf(f, "; %.*s\n", LIT(instr->Comment.text));
|
||||
break;
|
||||
|
||||
case ssaInstr_Local: {
|
||||
Type *type = instr->Local.entity->type;
|
||||
ssa_fprintf(f, "%%%d = alloca ", value->id);
|
||||
ssa_print_type(f, m->sizes, type);
|
||||
ssa_fprintf(f, ", align %lld ", type_align_of(m->sizes, m->allocator, type));
|
||||
{
|
||||
String str = instr->Local.entity->token.string;
|
||||
if (str.len > 0)
|
||||
ssa_fprintf(f, "; %.*s", LIT(instr->Local.entity->token.string));
|
||||
ssa_fprintf(f, ", align %lld\n", type_align_of(m->sizes, m->allocator, type));
|
||||
if (instr->Local.zero_initialized) {
|
||||
ssa_fprintf(f, "\tstore ");
|
||||
ssa_print_type(f, m->sizes, type);
|
||||
ssa_fprintf(f, " zeroinitializer, ");
|
||||
ssa_print_type(f, m->sizes, type);
|
||||
ssa_fprintf(f, "* %%%d\n", value->id);
|
||||
}
|
||||
ssa_fprintf(f, "\n");
|
||||
ssa_fprintf(f, "\tstore ");
|
||||
ssa_print_type(f, m->sizes, type);
|
||||
ssa_fprintf(f, " zeroinitializer, ");
|
||||
ssa_print_type(f, m->sizes, type);
|
||||
ssa_fprintf(f, "* %%%d\n", value->id);
|
||||
} break;
|
||||
|
||||
case ssaInstr_Store: {
|
||||
@@ -713,9 +713,9 @@ void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) {
|
||||
auto *params = &proc_type->params->Tuple;
|
||||
for (isize i = 0; i < params->variable_count; i++) {
|
||||
Entity *e = params->variables[i];
|
||||
if (i > 0)
|
||||
|
||||
if (i > 0) {
|
||||
ssa_fprintf(f, ", ");
|
||||
}
|
||||
ssa_print_type(f, m->sizes, e->type);
|
||||
ssa_fprintf(f, " %%%.*s", LIT(e->token.string));
|
||||
}
|
||||
@@ -724,14 +724,18 @@ void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) {
|
||||
ssa_fprintf(f, ") ");
|
||||
|
||||
if (proc->tags != 0) {
|
||||
if (proc->tags & ProcTag_inline)
|
||||
if (proc->tags & ProcTag_inline) {
|
||||
ssa_fprintf(f, "alwaysinline ");
|
||||
if (proc->tags & ProcTag_no_inline)
|
||||
}
|
||||
if (proc->tags & ProcTag_no_inline) {
|
||||
ssa_fprintf(f, "noinline ");
|
||||
}
|
||||
|
||||
|
||||
if (proc->tags & ProcTag_foreign)
|
||||
if (proc->tags & ProcTag_foreign) {
|
||||
// TODO(bill): Set calling convention
|
||||
ssa_fprintf(f, "; foreign\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (proc->body != NULL) {
|
||||
@@ -803,7 +807,10 @@ void ssa_print_llvm_ir(gbFile *f, ssaModule *m) {
|
||||
ssa_fprintf(f, "thread_local ");
|
||||
}
|
||||
if (g->is_constant) {
|
||||
ssa_fprintf(f, "private constant ");
|
||||
if (g->is_private) {
|
||||
ssa_fprintf(f, "private ");
|
||||
}
|
||||
ssa_fprintf(f, "constant ");
|
||||
} else {
|
||||
ssa_fprintf(f, "global ");
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ struct ssaProcedure {
|
||||
|
||||
#define SSA_INSTR_KINDS \
|
||||
SSA_INSTR_KIND(Invalid), \
|
||||
SSA_INSTR_KIND(Comment), \
|
||||
SSA_INSTR_KIND(Local), \
|
||||
SSA_INSTR_KIND(Store), \
|
||||
SSA_INSTR_KIND(Load), \
|
||||
@@ -138,9 +139,14 @@ struct ssaInstr {
|
||||
Type *type;
|
||||
|
||||
union {
|
||||
struct {
|
||||
String text;
|
||||
} Comment;
|
||||
|
||||
struct {
|
||||
Entity *entity;
|
||||
Type *type;
|
||||
Type * type;
|
||||
b32 zero_initialized;
|
||||
} Local;
|
||||
struct {
|
||||
ssaValue *address;
|
||||
@@ -251,6 +257,7 @@ struct ssaValue {
|
||||
} TypeName;
|
||||
struct {
|
||||
b32 is_constant;
|
||||
b32 is_private;
|
||||
b32 is_thread_local;
|
||||
Entity * entity;
|
||||
Type * type;
|
||||
@@ -435,11 +442,12 @@ ssaValue *ssa_make_value_param(gbAllocator a, ssaProcedure *parent, Entity *e) {
|
||||
}
|
||||
|
||||
|
||||
ssaValue *ssa_make_instr_local(ssaProcedure *p, Entity *e) {
|
||||
ssaValue *ssa_make_instr_local(ssaProcedure *p, Entity *e, b32 zero_initialized) {
|
||||
ssaValue *v = ssa_alloc_instr(p, ssaInstr_Local);
|
||||
ssaInstr *i = &v->Instr;
|
||||
i->Local.entity = e;
|
||||
i->Local.type = make_type_pointer(p->module->allocator, e->type);
|
||||
i->Local.zero_initialized = zero_initialized;
|
||||
ssa_module_add_value(p->module, e, v);
|
||||
return v;
|
||||
}
|
||||
@@ -589,6 +597,11 @@ ssaValue *ssa_make_instr_no_op(ssaProcedure *p) {
|
||||
}
|
||||
|
||||
|
||||
ssaValue *ssa_make_instr_comment(ssaProcedure *p, String text) {
|
||||
ssaValue *v = ssa_alloc_instr(p, ssaInstr_Comment);
|
||||
v->Instr.Comment.text = text;
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -681,15 +694,21 @@ ssaValue *ssa_emit_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue
|
||||
return ssa_emit(p, ssa_make_instr_select(p, cond, t, f));
|
||||
}
|
||||
|
||||
|
||||
ssaValue *ssa_add_local(ssaProcedure *proc, Entity *e) {
|
||||
return ssa_emit(proc, ssa_make_instr_local(proc, e));
|
||||
ssaValue *ssa_emit_comment(ssaProcedure *p, String text) {
|
||||
return ssa_emit(p, ssa_make_instr_comment(p, text));
|
||||
}
|
||||
|
||||
ssaValue *ssa_add_local_for_identifier(ssaProcedure *proc, AstNode *name) {
|
||||
|
||||
ssaValue *ssa_add_local(ssaProcedure *proc, Entity *e, b32 zero_initialized = true) {
|
||||
return ssa_emit(proc, ssa_make_instr_local(proc, e, zero_initialized));
|
||||
}
|
||||
|
||||
ssaValue *ssa_add_local_for_identifier(ssaProcedure *proc, AstNode *name, b32 zero_initialized) {
|
||||
Entity **found = map_get(&proc->module->info->definitions, hash_pointer(name));
|
||||
if (found) {
|
||||
return ssa_add_local(proc, *found);
|
||||
Entity *e = *found;
|
||||
ssa_emit_comment(proc, e->token.string);
|
||||
return ssa_add_local(proc, e, zero_initialized);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -705,7 +724,7 @@ ssaValue *ssa_add_local_generated(ssaProcedure *proc, Type *type) {
|
||||
scope,
|
||||
empty_token,
|
||||
type);
|
||||
return ssa_emit(proc, ssa_make_instr_local(proc, entity));
|
||||
return ssa_emit(proc, ssa_make_instr_local(proc, entity, true));
|
||||
}
|
||||
|
||||
ssaValue *ssa_add_param(ssaProcedure *proc, Entity *e) {
|
||||
@@ -772,6 +791,7 @@ void ssa_build_defer_stmt(ssaProcedure *proc, ssaDefer d) {
|
||||
}
|
||||
gb_array_append(proc->blocks, b);
|
||||
proc->curr_block = b;
|
||||
ssa_emit_comment(proc, make_string("defer"));
|
||||
ssa_build_stmt(proc, d.stmt);
|
||||
}
|
||||
|
||||
@@ -781,7 +801,7 @@ void ssa_emit_defer_stmts(ssaProcedure *proc, ssaDeferKind kind, ssaBlock *block
|
||||
while (i --> 0) {
|
||||
ssaDefer d = proc->defer_stmts[i];
|
||||
if (kind == ssaDefer_Return) {
|
||||
ssa_build_defer_stmt(proc, d);
|
||||
ssa_build_defer_stmt(proc, d);
|
||||
} else if (kind == ssaDefer_Default) {
|
||||
if (proc->scope_index == d.scope_index &&
|
||||
d.scope_index > 1) {
|
||||
@@ -899,6 +919,7 @@ void ssa_end_procedure_body(ssaProcedure *proc) {
|
||||
ssaInstr *instr = &value->Instr;
|
||||
// NOTE(bill): Ignore non-returning instructions
|
||||
switch (instr->kind) {
|
||||
case ssaInstr_Comment:
|
||||
case ssaInstr_Store:
|
||||
case ssaInstr_Br:
|
||||
case ssaInstr_Ret:
|
||||
@@ -1217,6 +1238,7 @@ ssaValue *ssa_add_global_string_array(ssaProcedure *proc, ExactValue value) {
|
||||
Type *type = make_type_array(a, t_u8, value.value_string.len);
|
||||
Entity *entity = make_entity_constant(a, NULL, token, type, value);
|
||||
ssaValue *g = ssa_make_value_global(a, entity, ssa_make_value_constant(a, type, value));
|
||||
g->Global.is_private = true;
|
||||
|
||||
map_set(&proc->module->values, hash_pointer(entity), g);
|
||||
map_set(&proc->module->members, hash_string(name), g);
|
||||
@@ -1368,6 +1390,7 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg
|
||||
for (isize i = 0; i < dst->Record.field_count; i++) {
|
||||
Entity *f = dst->Record.fields[i];
|
||||
if (are_types_identical(f->type, src_type)) {
|
||||
ssa_emit_comment(proc, make_string("union - child to parent"));
|
||||
gbAllocator allocator = proc->module->allocator;
|
||||
ssaValue *parent = ssa_add_local_generated(proc, t);
|
||||
ssaValue *tag = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(i));
|
||||
@@ -1404,6 +1427,7 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg
|
||||
// NOTE(bill): It can be casted
|
||||
Selection sel = lookup_field(sb, field_name, false);
|
||||
if (sel.entity != NULL) {
|
||||
ssa_emit_comment(proc, make_string("cast - polymorphism"));
|
||||
if (src_is_ptr) {
|
||||
value = ssa_emit_load(proc, value);
|
||||
}
|
||||
@@ -1621,12 +1645,15 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
} break;
|
||||
|
||||
case Token_as:
|
||||
ssa_emit_comment(proc, make_string("cast - as"));
|
||||
return ssa_emit_conv(proc, ssa_build_expr(proc, be->left), tv->type);
|
||||
|
||||
case Token_transmute:
|
||||
ssa_emit_comment(proc, make_string("cast - transmute"));
|
||||
return ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), tv->type);
|
||||
|
||||
case Token_down_cast:
|
||||
ssa_emit_comment(proc, make_string("cast - down_cast"));
|
||||
return ssa_emit_down_cast(proc, ssa_build_expr(proc, be->left), tv->type);
|
||||
|
||||
default:
|
||||
@@ -1660,6 +1687,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
|
||||
|
||||
case_ast_node(cl, CompoundLit, expr);
|
||||
ssa_emit_comment(proc, make_string("CompoundLit"));
|
||||
Type *type = type_of_expr(proc->module->info, expr);
|
||||
Type *base_type = get_base_type(type);
|
||||
ssaValue *v = ssa_add_local_generated(proc, type);
|
||||
@@ -1787,6 +1815,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
Entity *e = *found;
|
||||
switch (e->Builtin.id) {
|
||||
case BuiltinProc_new: {
|
||||
ssa_emit_comment(proc, make_string("new"));
|
||||
// new :: proc(Type) -> ^Type
|
||||
gbAllocator allocator = proc->module->allocator;
|
||||
|
||||
@@ -1805,6 +1834,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
} break;
|
||||
|
||||
case BuiltinProc_new_slice: {
|
||||
ssa_emit_comment(proc, make_string("new_slice"));
|
||||
// new_slice :: proc(Type, len: int[, cap: int]) -> ^Type
|
||||
gbAllocator allocator = proc->module->allocator;
|
||||
|
||||
@@ -1844,6 +1874,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
} break;
|
||||
|
||||
case BuiltinProc_delete: {
|
||||
ssa_emit_comment(proc, make_string("delete"));
|
||||
// delete :: proc(ptr: ^Type)
|
||||
// delete :: proc(slice: []Type)
|
||||
gbAllocator allocator = proc->module->allocator;
|
||||
@@ -1863,6 +1894,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
|
||||
|
||||
case BuiltinProc_assert: {
|
||||
ssa_emit_comment(proc, make_string("assert"));
|
||||
ssaValue *cond = ssa_build_expr(proc, ce->arg_list);
|
||||
GB_ASSERT(is_type_boolean(ssa_type(cond)));
|
||||
|
||||
@@ -1905,6 +1937,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
} break;
|
||||
|
||||
case BuiltinProc_len: {
|
||||
ssa_emit_comment(proc, make_string("len"));
|
||||
// len :: proc(v: Type) -> int
|
||||
// NOTE(bill): len of an array is a constant expression
|
||||
ssaValue *v = ssa_build_expr(proc, ce->arg_list);
|
||||
@@ -1915,6 +1948,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
return ssa_slice_len(proc, v);
|
||||
} break;
|
||||
case BuiltinProc_cap: {
|
||||
ssa_emit_comment(proc, make_string("cap"));
|
||||
// cap :: proc(v: Type) -> int
|
||||
// NOTE(bill): cap of an array is a constant expression
|
||||
ssaValue *v = ssa_build_expr(proc, ce->arg_list);
|
||||
@@ -1922,6 +1956,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
return ssa_slice_cap(proc, v);
|
||||
} break;
|
||||
case BuiltinProc_copy: {
|
||||
ssa_emit_comment(proc, make_string("copy"));
|
||||
// copy :: proc(dst, src: []Type) -> int
|
||||
AstNode *dst_node = ce->arg_list;
|
||||
AstNode *src_node = ce->arg_list->next;
|
||||
@@ -1955,6 +1990,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
return len;
|
||||
} break;
|
||||
case BuiltinProc_append: {
|
||||
ssa_emit_comment(proc, make_string("append"));
|
||||
// append :: proc(s: ^[]Type, item: Type) -> bool
|
||||
AstNode *sptr_node = ce->arg_list;
|
||||
AstNode *item_node = ce->arg_list->next;
|
||||
@@ -1993,7 +2029,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
|
||||
item = ssa_emit_ptr_offset(proc, item, v_zero);
|
||||
item = ssa_emit_conv(proc, item, t_rawptr, true);
|
||||
|
||||
|
||||
ssa_emit(proc, ssa_make_instr_copy_memory(proc, offset, item, byte_count, 1, false));
|
||||
|
||||
// Increment slice length
|
||||
@@ -2010,6 +2046,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
} break;
|
||||
|
||||
case BuiltinProc_swizzle: {
|
||||
ssa_emit_comment(proc, make_string("swizzle"));
|
||||
ssaValue *vector = ssa_build_expr(proc, ce->arg_list);
|
||||
isize index_count = ce->arg_list_count-1;
|
||||
if (index_count == 0) {
|
||||
@@ -2030,12 +2067,14 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
} break;
|
||||
|
||||
case BuiltinProc_ptr_offset: {
|
||||
ssa_emit_comment(proc, make_string("ptr_offset"));
|
||||
ssaValue *ptr = ssa_build_expr(proc, ce->arg_list);
|
||||
ssaValue *offset = ssa_build_expr(proc, ce->arg_list->next);
|
||||
return ssa_emit_ptr_offset(proc, ptr, offset);
|
||||
} break;
|
||||
|
||||
case BuiltinProc_ptr_sub: {
|
||||
ssa_emit_comment(proc, make_string("ptr_sub"));
|
||||
ssaValue *ptr_a = ssa_build_expr(proc, ce->arg_list);
|
||||
ssaValue *ptr_b = ssa_build_expr(proc, ce->arg_list->next);
|
||||
Type *ptr_type = get_base_type(ssa_type(ptr_a));
|
||||
@@ -2054,6 +2093,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
} break;
|
||||
|
||||
case BuiltinProc_slice_ptr: {
|
||||
ssa_emit_comment(proc, make_string("slice_ptr"));
|
||||
ssaValue *ptr = ssa_build_expr(proc, ce->arg_list);
|
||||
ssaValue *len = ssa_build_expr(proc, ce->arg_list->next);
|
||||
ssaValue *cap = len;
|
||||
@@ -2075,6 +2115,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
} break;
|
||||
|
||||
case BuiltinProc_min: {
|
||||
ssa_emit_comment(proc, make_string("min"));
|
||||
ssaValue *x = ssa_build_expr(proc, ce->arg_list);
|
||||
ssaValue *y = ssa_build_expr(proc, ce->arg_list->next);
|
||||
Type *t = get_base_type(ssa_type(x));
|
||||
@@ -2084,6 +2125,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
} break;
|
||||
|
||||
case BuiltinProc_max: {
|
||||
ssa_emit_comment(proc, make_string("max"));
|
||||
ssaValue *x = ssa_build_expr(proc, ce->arg_list);
|
||||
ssaValue *y = ssa_build_expr(proc, ce->arg_list->next);
|
||||
Type *t = get_base_type(ssa_type(x));
|
||||
@@ -2093,6 +2135,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
} break;
|
||||
|
||||
case BuiltinProc_abs: {
|
||||
ssa_emit_comment(proc, make_string("abs"));
|
||||
Token lt = {Token_Lt};
|
||||
Token sub = {Token_Sub};
|
||||
|
||||
@@ -2165,6 +2208,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
}
|
||||
|
||||
if (variadic) {
|
||||
ssa_emit_comment(proc, make_string("variadic call argument generation"));
|
||||
gbAllocator allocator = proc->module->allocator;
|
||||
Type *slice_type = pt->variables[type->param_count-1]->type;
|
||||
Type *elem_type = get_base_type(slice_type)->Slice.elem;
|
||||
@@ -2218,6 +2262,8 @@ ssaValue *ssa_build_expr(ssaProcedure *proc, AstNode *expr) {
|
||||
|
||||
if (tv->value.kind != ExactValue_Invalid) {
|
||||
if (tv->value.kind == ExactValue_String) {
|
||||
ssa_emit_comment(proc, make_string("Emit string constant"));
|
||||
|
||||
// TODO(bill): Optimize by not allocating everytime
|
||||
if (tv->value.value_string.len > 0) {
|
||||
ssaValue *global_array = ssa_add_global_string_array(proc, tv->value);
|
||||
@@ -2297,6 +2343,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(se, SelectorExpr, expr);
|
||||
ssa_emit_comment(proc, make_string("SelectorExpr"));
|
||||
Type *type = get_base_type(type_of_expr(proc->module->info, se->expr));
|
||||
|
||||
Selection sel = lookup_field(type, unparen_expr(se->selector)->Ident.string, false);
|
||||
@@ -2320,6 +2367,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
|
||||
case_ast_node(be, BinaryExpr, expr);
|
||||
switch (be->op.kind) {
|
||||
case Token_as: {
|
||||
ssa_emit_comment(proc, make_string("Cast - as"));
|
||||
// HACK(bill): Do have to make new variable to do this?
|
||||
// NOTE(bill): Needed for dereference of pointer conversion
|
||||
Type *type = type_of_expr(proc->module->info, expr);
|
||||
@@ -2328,6 +2376,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
|
||||
return ssa_make_addr(v, expr);
|
||||
}
|
||||
case Token_transmute: {
|
||||
ssa_emit_comment(proc, make_string("Cast - transmute"));
|
||||
// HACK(bill): Do have to make new variable to do this?
|
||||
// NOTE(bill): Needed for dereference of pointer conversion
|
||||
Type *type = type_of_expr(proc->module->info, expr);
|
||||
@@ -2342,6 +2391,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(ie, IndexExpr, expr);
|
||||
ssa_emit_comment(proc, make_string("IndexExpr"));
|
||||
ssaValue *v = NULL;
|
||||
Type *t = get_base_type(type_of_expr(proc->module->info, ie->expr));
|
||||
ssaValue *elem = NULL;
|
||||
@@ -2387,6 +2437,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(se, SliceExpr, expr);
|
||||
ssa_emit_comment(proc, make_string("SliceExpr"));
|
||||
gbAllocator a = proc->module->allocator;
|
||||
ssaValue *low = v_zero;
|
||||
ssaValue *high = NULL;
|
||||
@@ -2412,7 +2463,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
|
||||
ssaValue *len = ssa_emit_arith(proc, op_sub, high, low, t_int);
|
||||
ssaValue *cap = ssa_emit_arith(proc, op_sub, max, low, t_int);
|
||||
ssaValue *slice = ssa_add_local_generated(proc, slice_type);
|
||||
|
||||
|
||||
ssaValue *gep0 = ssa_emit_struct_gep(proc, slice, v_zero32, ssa_type(elem));
|
||||
ssaValue *gep1 = ssa_emit_struct_gep(proc, slice, v_one32, t_int);
|
||||
ssaValue *gep2 = ssa_emit_struct_gep(proc, slice, v_two32, t_int);
|
||||
@@ -2520,7 +2571,7 @@ void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) {
|
||||
map_set(&m->members, hash_string(name), t);
|
||||
|
||||
Type *bt = get_base_type(e->type);
|
||||
if (is_type_struct(bt)) {
|
||||
if (bt->kind == Type_Record) {
|
||||
auto *s = &bt->Record;
|
||||
for (isize j = 0; j < s->other_field_count; j++) {
|
||||
Entity *field = s->other_fields[j];
|
||||
@@ -2541,6 +2592,29 @@ void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_union(bt)) {
|
||||
auto *s = &bt->Record;
|
||||
// NOTE(bill): Zeroth entry is null (for `match type` stmts)
|
||||
for (isize j = 1; j < s->field_count; j++) {
|
||||
Entity *field = s->fields[j];
|
||||
if (field->kind == Entity_TypeName) {
|
||||
// HACK(bill): Override name of type so printer prints it correctly
|
||||
auto *tn = &field->type->Named;
|
||||
String cn = field->token.string;
|
||||
isize len = name.len + 1 + cn.len;
|
||||
String child = {NULL, len};
|
||||
child.text = gb_alloc_array(m->allocator, u8, len);
|
||||
isize i = 0;
|
||||
gb_memcopy(child.text+i, name.text, name.len);
|
||||
i += name.len;
|
||||
child.text[i++] = '.';
|
||||
gb_memcopy(child.text+i, cn.text, cn.len);
|
||||
tn->name = child;
|
||||
ssa_gen_global_type_name(m, field, tn->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2575,7 +2649,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
|
||||
ssaAddr lval = ssa_make_addr(NULL, NULL);
|
||||
if (!ssa_is_blank_ident(name)) {
|
||||
ssa_add_local_for_identifier(proc, name);
|
||||
ssa_add_local_for_identifier(proc, name, false);
|
||||
lval = ssa_build_addr(proc, name);
|
||||
GB_ASSERT(lval.addr != NULL);
|
||||
}
|
||||
@@ -2597,7 +2671,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
} else if (vd->value_count == 0) { // declared and zero-initialized
|
||||
for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
|
||||
if (!ssa_is_blank_ident(name)) {
|
||||
ssa_add_local_for_identifier(proc, name);
|
||||
ssa_add_local_for_identifier(proc, name, true);
|
||||
}
|
||||
}
|
||||
} else { // Tuple(s)
|
||||
@@ -2611,7 +2685,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
|
||||
ssaAddr lval = ssa_make_addr(NULL, NULL);
|
||||
if (!ssa_is_blank_ident(name)) {
|
||||
ssa_add_local_for_identifier(proc, name);
|
||||
ssa_add_local_for_identifier(proc, name, false);
|
||||
lval = ssa_build_addr(proc, name);
|
||||
}
|
||||
|
||||
@@ -2669,6 +2743,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
gb_array_append(proc->children, &value->Proc);
|
||||
ssa_build_proc(value, proc);
|
||||
} else {
|
||||
// FFI - Foreign function interace
|
||||
String original_name = pd->name->Ident.string;
|
||||
String name = original_name;
|
||||
if (pd->foreign_name.len > 0) {
|
||||
@@ -2676,14 +2751,16 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
}
|
||||
auto *info = proc->module->info;
|
||||
|
||||
Entity **found = map_get(&info->definitions, hash_pointer(pd->name));
|
||||
GB_ASSERT(found != NULL);
|
||||
Entity *e = *found;
|
||||
Entity *e = *map_get(&info->definitions, hash_pointer(pd->name));
|
||||
Entity *f = *map_get(&info->foreign_procs, hash_string(name));
|
||||
ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
|
||||
proc->module, e->type, pd->type, pd->body, name);
|
||||
ssa_module_add_value(proc->module, e, value);
|
||||
gb_array_append(proc->children, &value->Proc);
|
||||
ssa_build_proc(value, proc);
|
||||
if (e == f) {
|
||||
// NOTE(bill): Don't do mutliple declarations in the IR
|
||||
gb_array_append(proc->children, &value->Proc);
|
||||
}
|
||||
}
|
||||
case_end;
|
||||
|
||||
@@ -2709,6 +2786,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(ids, IncDecStmt, node);
|
||||
ssa_emit_comment(proc, make_string("IncDecStmt"));
|
||||
Token op = ids->op;
|
||||
if (op.kind == Token_Increment) {
|
||||
op.kind = Token_Add;
|
||||
@@ -2722,6 +2800,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(as, AssignStmt, node);
|
||||
ssa_emit_comment(proc, make_string("AssignStmt"));
|
||||
switch (as->op.kind) {
|
||||
case Token_Eq: {
|
||||
gbArray(ssaAddr) lvals;
|
||||
@@ -2811,6 +2890,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(ds, DeferStmt, node);
|
||||
ssa_emit_comment(proc, make_string("DeferStmt"));
|
||||
isize scope_index = proc->scope_index;
|
||||
if (ds->stmt->kind == AstNode_BlockStmt)
|
||||
scope_index--;
|
||||
@@ -2819,6 +2899,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(rs, ReturnStmt, node);
|
||||
ssa_emit_comment(proc, make_string("ReturnStmt"));
|
||||
ssaValue *v = NULL;
|
||||
auto *return_type_tuple = &proc->type->Proc.results->Tuple;
|
||||
isize return_count = proc->type->Proc.result_count;
|
||||
@@ -2850,6 +2931,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(is, IfStmt, node);
|
||||
ssa_emit_comment(proc, make_string("IfStmt"));
|
||||
if (is->init != NULL) {
|
||||
ssaBlock *init = ssa_add_block(proc, node, make_string("if.init"));
|
||||
ssa_emit_jump(proc, init);
|
||||
@@ -2889,6 +2971,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(fs, ForStmt, node);
|
||||
ssa_emit_comment(proc, make_string("ForStmt"));
|
||||
if (fs->init != NULL) {
|
||||
ssaBlock *init = ssa_add_block(proc, node, make_string("for.init"));
|
||||
ssa_emit_jump(proc, init);
|
||||
@@ -2938,6 +3021,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(ms, MatchStmt, node);
|
||||
ssa_emit_comment(proc, make_string("MatchStmt"));
|
||||
if (ms->init != NULL) {
|
||||
ssa_build_stmt(proc, ms->init);
|
||||
}
|
||||
@@ -3030,6 +3114,113 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
proc->curr_block = done;
|
||||
case_end;
|
||||
|
||||
|
||||
case_ast_node(ms, TypeMatchStmt, node);
|
||||
ssa_emit_comment(proc, make_string("TypeMatchStmt"));
|
||||
gbAllocator allocator = proc->module->allocator;
|
||||
|
||||
ssaValue *parent = ssa_build_expr(proc, ms->tag);
|
||||
Type *union_type = type_deref(ssa_type(parent));
|
||||
GB_ASSERT(is_type_union(union_type));
|
||||
|
||||
ssaValue *tag_index = ssa_emit_struct_gep(proc, parent, v_zero32, make_type_pointer(allocator, t_int));
|
||||
tag_index = ssa_emit_load(proc, tag_index);
|
||||
|
||||
i64 tag_size = proc->module->sizes.word_size;
|
||||
i64 underlying_array_size = type_size_of(proc->module->sizes, allocator, union_type);
|
||||
underlying_array_size -= tag_size;
|
||||
Type *array_type = make_type_array(allocator, t_u8, underlying_array_size);
|
||||
Type *array_type_ptr = make_type_pointer(allocator, array_type);
|
||||
ssaValue *data = ssa_emit_struct_gep(proc, parent, v_one32, array_type_ptr);
|
||||
data = ssa_array_elem(proc, data);
|
||||
|
||||
ssaBlock *done = ssa__make_block(proc, node, make_string("type.match.done")); // NOTE(bill): Append later
|
||||
|
||||
ast_node(body, BlockStmt, ms->body);
|
||||
|
||||
|
||||
String tag_var_name = ms->var->Ident.string;
|
||||
|
||||
AstNode *default_stmts = NULL;
|
||||
ssaBlock *default_block = NULL;
|
||||
|
||||
isize case_count = body->list_count;
|
||||
isize i = 0;
|
||||
for (AstNode *clause = body->list;
|
||||
clause != NULL;
|
||||
clause = clause->next, i++) {
|
||||
ssaBlock *body = NULL;
|
||||
b32 append_body = false;
|
||||
|
||||
ast_node(cc, CaseClause, clause);
|
||||
|
||||
if (body == NULL) {
|
||||
append_body = true;
|
||||
if (cc->list == NULL) {
|
||||
body = ssa__make_block(proc, clause, make_string("type.match.dflt.body"));
|
||||
} else {
|
||||
body = ssa__make_block(proc, clause, make_string("type.match.case.body"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (cc->list == NULL) {
|
||||
// default case
|
||||
default_stmts = cc->stmts;
|
||||
default_block = body;
|
||||
continue;
|
||||
}
|
||||
|
||||
Scope *scope = *map_get(&proc->module->info->scopes, hash_pointer(clause));
|
||||
Entity *tag_var_entity = current_scope_lookup_entity(scope, tag_var_name);
|
||||
ssaValue *tag_var = ssa_add_local(proc, tag_var_entity);
|
||||
ssaValue *data_ptr = ssa_emit_conv(proc, data, tag_var_entity->type);
|
||||
ssa_emit_store(proc, tag_var, data_ptr);
|
||||
|
||||
|
||||
|
||||
Type *base_type = type_deref(tag_var_entity->type);
|
||||
ssaValue *index = NULL;
|
||||
for (isize i = 0; i < get_base_type(union_type)->Record.field_count; i++) {
|
||||
Entity *f = get_base_type(union_type)->Record.fields[i];
|
||||
if (are_types_identical(f->type, base_type)) {
|
||||
index = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(i));
|
||||
}
|
||||
}
|
||||
GB_ASSERT(index != NULL);
|
||||
|
||||
ssaBlock *next_cond = ssa__make_block(proc, clause, make_string("type.match.case.next"));
|
||||
Token eq = {Token_CmpEq};
|
||||
ssaValue *cond = ssa_emit_comp(proc, eq, tag_index, index);
|
||||
ssa_emit_if(proc, cond, body, next_cond);
|
||||
gb_array_append(proc->blocks, next_cond);
|
||||
proc->curr_block = next_cond;
|
||||
|
||||
if (append_body) {
|
||||
gb_array_append(proc->blocks, body);
|
||||
}
|
||||
proc->curr_block = body;
|
||||
ssa_push_target_list(proc, done, NULL, NULL);
|
||||
ssa_build_stmt_list(proc, cc->stmts);
|
||||
ssa_pop_target_list(proc);
|
||||
ssa_emit_jump(proc, done);
|
||||
proc->curr_block = next_cond;
|
||||
}
|
||||
|
||||
if (default_block != NULL) {
|
||||
ssa_emit_jump(proc, default_block);
|
||||
gb_array_append(proc->blocks, default_block);
|
||||
proc->curr_block = default_block;
|
||||
ssa_push_target_list(proc, done, NULL, NULL);
|
||||
ssa_build_stmt_list(proc, default_stmts);
|
||||
ssa_pop_target_list(proc);
|
||||
}
|
||||
|
||||
ssa_emit_jump(proc, done);
|
||||
gb_array_append(proc->blocks, done);
|
||||
proc->curr_block = done;
|
||||
case_end;
|
||||
|
||||
case_ast_node(bs, BranchStmt, node);
|
||||
ssaBlock *block = NULL;
|
||||
switch (bs->token.kind) {
|
||||
@@ -3052,6 +3243,11 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
if (block != NULL && bs->token.kind != Token_fallthrough) {
|
||||
ssa_emit_defer_stmts(proc, ssaDefer_Branch, block);
|
||||
}
|
||||
switch (bs->token.kind) {
|
||||
case Token_break: ssa_emit_comment(proc, make_string("break")); break;
|
||||
case Token_continue: ssa_emit_comment(proc, make_string("continue")); break;
|
||||
case Token_fallthrough: ssa_emit_comment(proc, make_string("fallthrough")); break;
|
||||
}
|
||||
ssa_emit_jump(proc, block);
|
||||
ssa_emit_unreachable(proc);
|
||||
case_end;
|
||||
|
||||
127
src/parser.cpp
127
src/parser.cpp
@@ -164,6 +164,11 @@ AST_NODE_KIND(_ComplexStmtBegin, "", struct{}) \
|
||||
AstNode *init, *tag; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(TypeMatchStmt, "type match statement", struct { \
|
||||
Token token; \
|
||||
AstNode *tag, *var; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \
|
||||
AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \
|
||||
AST_NODE_KIND(UsingStmt, "using statement", struct { Token token; AstNode *node; }) \
|
||||
@@ -682,6 +687,16 @@ gb_inline AstNode *make_match_stmt(AstFile *f, Token token, AstNode *init, AstNo
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
gb_inline AstNode *make_type_match_stmt(AstFile *f, Token token, AstNode *tag, AstNode *var, AstNode *body) {
|
||||
AstNode *result = make_node(f, AstNode_TypeMatchStmt);
|
||||
result->TypeMatchStmt.token = token;
|
||||
result->TypeMatchStmt.tag = tag;
|
||||
result->TypeMatchStmt.var = var;
|
||||
result->TypeMatchStmt.body = body;
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_inline AstNode *make_case_clause(AstFile *f, Token token, AstNode *list, isize list_count, AstNode *stmts, isize stmt_count) {
|
||||
AstNode *result = make_node(f, AstNode_CaseClause);
|
||||
result->CaseClause.token = token;
|
||||
@@ -1399,10 +1414,7 @@ AstNode *parse_atom_expr(AstFile *f, b32 lhs) {
|
||||
break;
|
||||
|
||||
case Token_OpenBrace: {
|
||||
if (is_literal_type(operand) && f->expr_level >= 0) {
|
||||
if (lhs) {
|
||||
// TODO(bill): Handle this
|
||||
}
|
||||
if (!lhs && is_literal_type(operand) && f->expr_level >= 0) {
|
||||
operand = parse_literal_value(f, operand);
|
||||
} else {
|
||||
loop = false;
|
||||
@@ -1432,7 +1444,7 @@ AstNode *parse_unary_expr(AstFile *f, b32 lhs) {
|
||||
AstNode *operand;
|
||||
Token op = f->cursor[0];
|
||||
next_token(f);
|
||||
operand = parse_unary_expr(f, false);
|
||||
operand = parse_unary_expr(f, lhs);
|
||||
return make_unary_expr(f, op, operand);
|
||||
} break;
|
||||
}
|
||||
@@ -2247,6 +2259,23 @@ AstNode *parse_case_clause(AstFile *f) {
|
||||
return make_case_clause(f, token, list, list_count, stmts, stmt_count);
|
||||
}
|
||||
|
||||
|
||||
AstNode *parse_type_case_clause(AstFile *f) {
|
||||
Token token = f->cursor[0];
|
||||
AstNode *clause = NULL;
|
||||
if (allow_token(f, Token_case)) {
|
||||
clause = parse_expr(f, false);
|
||||
} else {
|
||||
expect_token(f, Token_default);
|
||||
}
|
||||
expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax?
|
||||
isize stmt_count = 0;
|
||||
AstNode *stmts = parse_stmt_list(f, &stmt_count);
|
||||
|
||||
return make_case_clause(f, token, clause, 1, stmts, stmt_count);
|
||||
}
|
||||
|
||||
|
||||
AstNode *parse_match_stmt(AstFile *f) {
|
||||
if (f->curr_proc == NULL) {
|
||||
ast_file_err(f, f->cursor[0], "You cannot use a match statement in the file scope");
|
||||
@@ -2254,50 +2283,66 @@ AstNode *parse_match_stmt(AstFile *f) {
|
||||
}
|
||||
|
||||
Token token = expect_token(f, Token_match);
|
||||
|
||||
AstNode *init = NULL;
|
||||
AstNode *tag = NULL;
|
||||
if (f->cursor[0].kind != Token_OpenBrace) {
|
||||
isize prev_level = f->expr_level;
|
||||
f->expr_level = -1;
|
||||
if (f->cursor[0].kind != Token_Semicolon) {
|
||||
tag = parse_simple_stmt(f);
|
||||
AstNode *body = NULL;
|
||||
Token open, close;
|
||||
|
||||
if (allow_token(f, Token_type)) {
|
||||
tag = parse_expr(f, true);
|
||||
expect_token(f, Token_ArrowRight);
|
||||
AstNode *var = parse_identifier(f);
|
||||
|
||||
open = expect_token(f, Token_OpenBrace);
|
||||
AstNode *list = NULL;
|
||||
AstNode *list_curr = NULL;
|
||||
isize list_count = 0;
|
||||
|
||||
while (f->cursor[0].kind == Token_case ||
|
||||
f->cursor[0].kind == Token_default) {
|
||||
DLIST_APPEND(list, list_curr, parse_type_case_clause(f));
|
||||
list_count++;
|
||||
}
|
||||
if (allow_token(f, Token_Semicolon)) {
|
||||
init = tag;
|
||||
tag = NULL;
|
||||
if (f->cursor[0].kind != Token_OpenBrace) {
|
||||
|
||||
close = expect_token(f, Token_CloseBrace);
|
||||
body = make_block_stmt(f, list, list_count, open, close);
|
||||
return make_type_match_stmt(f, token, tag, var, body);
|
||||
} else {
|
||||
if (f->cursor[0].kind != Token_OpenBrace) {
|
||||
isize prev_level = f->expr_level;
|
||||
f->expr_level = -1;
|
||||
if (f->cursor[0].kind != Token_Semicolon) {
|
||||
tag = parse_simple_stmt(f);
|
||||
}
|
||||
if (allow_token(f, Token_Semicolon)) {
|
||||
init = tag;
|
||||
tag = NULL;
|
||||
if (f->cursor[0].kind != Token_OpenBrace) {
|
||||
tag = parse_simple_stmt(f);
|
||||
}
|
||||
}
|
||||
|
||||
f->expr_level = prev_level;
|
||||
}
|
||||
|
||||
f->expr_level = prev_level;
|
||||
open = expect_token(f, Token_OpenBrace);
|
||||
AstNode *list = NULL;
|
||||
AstNode *list_curr = NULL;
|
||||
isize list_count = 0;
|
||||
|
||||
while (f->cursor[0].kind == Token_case ||
|
||||
f->cursor[0].kind == Token_default) {
|
||||
DLIST_APPEND(list, list_curr, parse_case_clause(f));
|
||||
list_count++;
|
||||
}
|
||||
|
||||
close = expect_token(f, Token_CloseBrace);
|
||||
|
||||
body = make_block_stmt(f, list, list_count, open, close);
|
||||
|
||||
tag = convert_stmt_to_expr(f, tag, make_string("match expression"));
|
||||
return make_match_stmt(f, token, init, tag, body);
|
||||
}
|
||||
|
||||
Token open = expect_token(f, Token_OpenBrace);
|
||||
AstNode *list = NULL;
|
||||
AstNode *list_curr = NULL;
|
||||
isize list_count = 0;
|
||||
#if 1
|
||||
while (f->cursor[0].kind == Token_case ||
|
||||
f->cursor[0].kind == Token_default) {
|
||||
DLIST_APPEND(list, list_curr, parse_case_clause(f));
|
||||
list_count++;
|
||||
}
|
||||
#else
|
||||
|
||||
while (f->cursor[0].kind == Token_ArrowRight) {
|
||||
DLIST_APPEND(list, list_curr, parse_case_clause(f));
|
||||
list_count++;
|
||||
}
|
||||
|
||||
#endif
|
||||
Token close = expect_token(f, Token_CloseBrace);
|
||||
|
||||
AstNode *body = make_block_stmt(f, list, list_count, open, close);
|
||||
|
||||
tag = convert_stmt_to_expr(f, tag, make_string("match expression"));
|
||||
return make_match_stmt(f, token, init, tag, body);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user