analysis: add check of composite types (structs and enums)
- analysis: add bool check for loop condition exprs
This commit is contained in:
parent
dfb954c39c
commit
6543884366
2 changed files with 81 additions and 11 deletions
|
@ -17,8 +17,13 @@ enum B {
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum C {
|
||||||
|
blah
|
||||||
|
bluh
|
||||||
|
}
|
||||||
|
|
||||||
fn test_function() B {
|
fn test_function() B {
|
||||||
return B.a;
|
return B.bluh;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multwo(num: i32, double_flag: bool) i32 {
|
fn multwo(num: i32, double_flag: bool) i32 {
|
||||||
|
|
|
@ -88,14 +88,75 @@ pub const TypeSolver = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expectSymUnType(self: *@This(), symbol_type: comp.SymbolUnderlyingType, wanted_type: comp.SymbolUnderlyingTypeEnum) !void {
|
/// Check if the given symbol type matches a given category.
|
||||||
var actual_type = comp.SymbolUnderlyingTypeEnum(symbol_type);
|
/// Does not validate equality of Structs and Enums.
|
||||||
if (actual_type != wanted_type) {
|
pub fn expectSymUnTypeEnum(
|
||||||
std.debug.warn("Expected {}, got {}\n", wanted_type, actual_type);
|
self: *@This(),
|
||||||
|
symbol_type: comp.SymbolUnderlyingType,
|
||||||
|
wanted_type_enum: comp.SymbolUnderlyingTypeEnum,
|
||||||
|
) !void {
|
||||||
|
var actual_enum = comp.SymbolUnderlyingTypeEnum(symbol_type);
|
||||||
|
if (actual_enum != wanted_type_enum) {
|
||||||
|
std.debug.warn("Expected {}, got {}\n", wanted_type_enum, actual_enum);
|
||||||
return CompileError.TypeError;
|
return CompileError.TypeError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compositeIdentifierEqual(
|
||||||
|
self: *@This(),
|
||||||
|
typ_enum: comp.SymbolUnderlyingTypeEnum,
|
||||||
|
sym_ident: []const u8,
|
||||||
|
expected_ident: []const u8,
|
||||||
|
) !void {
|
||||||
|
if (!std.mem.eql(u8, sym_ident, expected_ident)) {
|
||||||
|
self.doError(
|
||||||
|
"Expected {} {}, got {}",
|
||||||
|
@tagName(typ_enum),
|
||||||
|
expected_ident,
|
||||||
|
sym_ident,
|
||||||
|
);
|
||||||
|
|
||||||
|
return CompileError.TypeError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the given type matches the given expected type.
|
||||||
|
/// This does proper validation of the types if they're structs or enums.
|
||||||
|
pub fn expectSymUnTypeEqual(
|
||||||
|
self: *@This(),
|
||||||
|
symbol_type: comp.SymbolUnderlyingType,
|
||||||
|
expected_type: comp.SymbolUnderlyingType,
|
||||||
|
) !void {
|
||||||
|
const symbol_enum = comp.SymbolUnderlyingTypeEnum(symbol_type);
|
||||||
|
const expected_enum = comp.SymbolUnderlyingTypeEnum(expected_type);
|
||||||
|
|
||||||
|
if (symbol_enum != expected_enum) {
|
||||||
|
std.debug.warn("Expected {}, got {}\n", expected_enum, symbol_enum);
|
||||||
|
return CompileError.TypeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for most cases, this is already enough, however, for
|
||||||
|
// composite/abstraction types (structs & enums) we must check
|
||||||
|
// if they're actually equal types inside
|
||||||
|
|
||||||
|
switch (expected_type) {
|
||||||
|
.Struct => |expected_identifier| try self.compositeIdentifierEqual(
|
||||||
|
.Struct,
|
||||||
|
symbol_type.Struct,
|
||||||
|
expected_identifier,
|
||||||
|
),
|
||||||
|
|
||||||
|
.Enum => |expected_identifier| try self.compositeIdentifierEqual(
|
||||||
|
.Enum,
|
||||||
|
symbol_type.Enum,
|
||||||
|
expected_identifier,
|
||||||
|
),
|
||||||
|
|
||||||
|
// for everything else, an enum equality test is enough
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO make return type optional and so, skip exprs that
|
// TODO make return type optional and so, skip exprs that
|
||||||
// fail to be fully resolved, instead of returning CompileError
|
// fail to be fully resolved, instead of returning CompileError
|
||||||
pub fn resolveExprType(
|
pub fn resolveExprType(
|
||||||
|
@ -193,7 +254,10 @@ pub const TypeSolver = struct {
|
||||||
// TODO we need to fetch the given
|
// TODO we need to fetch the given
|
||||||
// struct field (on get.name) type and return it
|
// struct field (on get.name) type and return it
|
||||||
.Struct => @panic("TODO analysis of struct"),
|
.Struct => @panic("TODO analysis of struct"),
|
||||||
|
|
||||||
|
// TODO check if get.name exists in enum
|
||||||
.Enum => return global_typ,
|
.Enum => return global_typ,
|
||||||
|
|
||||||
else => {
|
else => {
|
||||||
std.debug.warn(
|
std.debug.warn(
|
||||||
"Expected Struct/Enum as get target, got {}\n",
|
"Expected Struct/Enum as get target, got {}\n",
|
||||||
|
@ -238,13 +302,13 @@ pub const TypeSolver = struct {
|
||||||
// pull a hack with err contexts, lol)
|
// pull a hack with err contexts, lol)
|
||||||
.Return => |ret| {
|
.Return => |ret| {
|
||||||
var ret_stmt_type = try self.resolveExprType(ctx, ret.value);
|
var ret_stmt_type = try self.resolveExprType(ctx, ret.value);
|
||||||
try self.expectSymUnType(ret_stmt_type, ctx.cur_function.?.return_type);
|
try self.expectSymUnTypeEqual(ret_stmt_type, ctx.cur_function.?.return_type);
|
||||||
},
|
},
|
||||||
|
|
||||||
// If create two scopes for each branch of the if
|
// If create two scopes for each branch of the if
|
||||||
.If => |ifstmt| {
|
.If => |ifstmt| {
|
||||||
var cond_type = try self.resolveExprType(ctx, ifstmt.condition);
|
var cond_type = try self.resolveExprType(ctx, ifstmt.condition);
|
||||||
try self.expectSymUnType(cond_type, .Bool);
|
try self.expectSymUnTypeEnum(cond_type, .Bool);
|
||||||
|
|
||||||
try ctx.bumpScope("if_then");
|
try ctx.bumpScope("if_then");
|
||||||
|
|
||||||
|
@ -267,9 +331,10 @@ pub const TypeSolver = struct {
|
||||||
// Loop (creates 1 scope) asserts that the expression
|
// Loop (creates 1 scope) asserts that the expression
|
||||||
// type is a bool
|
// type is a bool
|
||||||
.Loop => |loop| {
|
.Loop => |loop| {
|
||||||
if (loop.condition) |cond|
|
if (loop.condition) |cond| {
|
||||||
// TODO assert condition's type is bool
|
var expr = try self.resolveExprType(ctx, cond);
|
||||||
_ = try self.resolveExprType(ctx, cond);
|
try self.expectSymUnTypeEnum(expr, .Bool);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO bump-dump scope
|
// TODO bump-dump scope
|
||||||
for (loop.then_branch.toSlice()) |then_stmt| {
|
for (loop.then_branch.toSlice()) |then_stmt| {
|
||||||
|
@ -303,7 +368,7 @@ pub const TypeSolver = struct {
|
||||||
self.setErrContext("function {}", name);
|
self.setErrContext("function {}", name);
|
||||||
var ret_type = self.resolveGlobalType(ctx, decl.return_type.lexeme);
|
var ret_type = self.resolveGlobalType(ctx, decl.return_type.lexeme);
|
||||||
|
|
||||||
std.debug.warn("start analysis of fn {} ret_type: {}\n", decl.func_name.lexeme, ret_type);
|
std.debug.warn("start analysis of fn {}, ret type: {}\n", decl.func_name.lexeme, ret_type);
|
||||||
|
|
||||||
var parameters = comp.TypeList.init(self.allocator);
|
var parameters = comp.TypeList.init(self.allocator);
|
||||||
for (decl.params.toSlice()) |param| {
|
for (decl.params.toSlice()) |param| {
|
||||||
|
|
Loading…
Reference in a new issue