analysis: add check of composite types (structs and enums)

- analysis: add bool check for loop condition exprs
This commit is contained in:
Luna 2019-09-26 22:22:11 -03:00
parent dfb954c39c
commit 6543884366
2 changed files with 81 additions and 11 deletions

View file

@ -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 {

View file

@ -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| {