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
|
||||
}
|
||||
|
||||
enum C {
|
||||
blah
|
||||
bluh
|
||||
}
|
||||
|
||||
fn test_function() B {
|
||||
return B.a;
|
||||
return B.bluh;
|
||||
}
|
||||
|
||||
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 {
|
||||
var actual_type = comp.SymbolUnderlyingTypeEnum(symbol_type);
|
||||
if (actual_type != wanted_type) {
|
||||
std.debug.warn("Expected {}, got {}\n", wanted_type, actual_type);
|
||||
/// Check if the given symbol type matches a given category.
|
||||
/// Does not validate equality of Structs and Enums.
|
||||
pub fn expectSymUnTypeEnum(
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// fail to be fully resolved, instead of returning CompileError
|
||||
pub fn resolveExprType(
|
||||
|
@ -193,7 +254,10 @@ pub const TypeSolver = struct {
|
|||
// TODO we need to fetch the given
|
||||
// struct field (on get.name) type and return it
|
||||
.Struct => @panic("TODO analysis of struct"),
|
||||
|
||||
// TODO check if get.name exists in enum
|
||||
.Enum => return global_typ,
|
||||
|
||||
else => {
|
||||
std.debug.warn(
|
||||
"Expected Struct/Enum as get target, got {}\n",
|
||||
|
@ -238,13 +302,13 @@ pub const TypeSolver = struct {
|
|||
// pull a hack with err contexts, lol)
|
||||
.Return => |ret| {
|
||||
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 => |ifstmt| {
|
||||
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");
|
||||
|
||||
|
@ -267,9 +331,10 @@ pub const TypeSolver = struct {
|
|||
// Loop (creates 1 scope) asserts that the expression
|
||||
// type is a bool
|
||||
.Loop => |loop| {
|
||||
if (loop.condition) |cond|
|
||||
// TODO assert condition's type is bool
|
||||
_ = try self.resolveExprType(ctx, cond);
|
||||
if (loop.condition) |cond| {
|
||||
var expr = try self.resolveExprType(ctx, cond);
|
||||
try self.expectSymUnTypeEnum(expr, .Bool);
|
||||
}
|
||||
|
||||
// TODO bump-dump scope
|
||||
for (loop.then_branch.toSlice()) |then_stmt| {
|
||||
|
@ -303,7 +368,7 @@ pub const TypeSolver = struct {
|
|||
self.setErrContext("function {}", name);
|
||||
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);
|
||||
for (decl.params.toSlice()) |param| {
|
||||
|
|
Loading…
Reference in a new issue