Skip to content

Commit

Permalink
Parser: validate expression types in statements
Browse files Browse the repository at this point in the history
  • Loading branch information
Vexu committed Aug 12, 2021
1 parent c887ffc commit cee0161
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 11 deletions.
6 changes: 6 additions & 0 deletions src/Diagnostics.zig
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ pub const Tag = enum {
invalid_subscript,
array_after,
array_before,
statement_int,
statement_scalar,
};

const Options = struct {
Expand Down Expand Up @@ -469,6 +471,8 @@ pub fn renderExtra(comp: *Compilation, m: anytype) void {
.invalid_subscript => m.write("subscripted value is not an array or pointer"),
.array_after => m.print("array index {d} is past the end of the array", .{msg.extra.unsigned}),
.array_before => m.print("array index {d} is before the beginning of the array", .{msg.extra.signed}),
.statement_int => m.print("statement requires expression with integer type ('{s}' invalid)", .{msg.extra.str}),
.statement_scalar => m.print("statement requires expression with scalar type ('{s}' invalid)", .{msg.extra.str}),
}
m.end(lcs);

Expand Down Expand Up @@ -621,6 +625,8 @@ fn tagKind(diag: *Diagnostics, tag: Tag) Kind {
.invalid_cast_type,
.invalid_index,
.invalid_subscript,
.statement_int,
.statement_scalar,
=> .@"error",
.to_match_paren,
.to_match_brace,
Expand Down
44 changes: 34 additions & 10 deletions src/Parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,7 @@ fn typeQual(p: *Parser, ty: *Type) Error!bool {
try p.errStr(.duplicate_decl_spec, p.tok_i, "atomic")
else
ty.qual.atomic = true;
// TODO check that the type can be atomic
},
else => break,
}
Expand Down Expand Up @@ -1791,9 +1792,13 @@ fn stmt(p: *Parser) Error!NodeIndex {
defer p.scopes.items.len = start_scopes_len;

const l_paren = try p.expectToken(.l_paren);
const cond = try p.expr();
// TODO validate type
var cond = try p.expr();
try cond.expect(p);
try cond.lvalConversion(p);
if (cond.ty.isInt())
try cond.intCast(p, cond.ty.integerPromotion(p.pp.comp))
else if (!cond.ty.isFloat() and cond.ty.specifier != .pointer)
try p.errStr(.statement_scalar, l_paren + 1, try p.typeStr(cond.ty));
try p.expectClosing(l_paren, .r_paren);

const then = try p.stmt();
Expand All @@ -1820,9 +1825,13 @@ fn stmt(p: *Parser) Error!NodeIndex {
defer p.scopes.items.len = start_scopes_len;

const l_paren = try p.expectToken(.l_paren);
const cond = try p.expr();
// TODO validate type
var cond = try p.expr();
try cond.expect(p);
try cond.lvalConversion(p);
if (cond.ty.isInt())
try cond.intCast(p, cond.ty.integerPromotion(p.pp.comp))
else
try p.errStr(.statement_int, l_paren + 1, try p.typeStr(cond.ty));
try p.expectClosing(l_paren, .r_paren);

var switch_scope = Scope.Switch{
Expand All @@ -1842,9 +1851,13 @@ fn stmt(p: *Parser) Error!NodeIndex {
defer p.scopes.items.len = start_scopes_len;

const l_paren = try p.expectToken(.l_paren);
const cond = try p.expr();
// TODO validate type
var cond = try p.expr();
try cond.expect(p);
try cond.lvalConversion(p);
if (cond.ty.isInt())
try cond.intCast(p, cond.ty.integerPromotion(p.pp.comp))
else if (!cond.ty.isFloat() and cond.ty.specifier != .pointer)
try p.errStr(.statement_scalar, l_paren + 1, try p.typeStr(cond.ty));
try p.expectClosing(l_paren, .r_paren);

try p.scopes.append(.loop);
Expand All @@ -1865,9 +1878,13 @@ fn stmt(p: *Parser) Error!NodeIndex {

_ = try p.expectToken(.keyword_while);
const l_paren = try p.expectToken(.l_paren);
const cond = try p.expr();
// TODO validate type
var cond = try p.expr();
try cond.expect(p);
try cond.lvalConversion(p);
if (cond.ty.isInt())
try cond.intCast(p, cond.ty.integerPromotion(p.pp.comp))
else if (!cond.ty.isFloat() and cond.ty.specifier != .pointer)
try p.errStr(.statement_scalar, l_paren + 1, try p.typeStr(cond.ty));
try p.expectClosing(l_paren, .r_paren);

_ = try p.expectToken(.semicolon);
Expand All @@ -1892,7 +1909,14 @@ fn stmt(p: *Parser) Error!NodeIndex {
if (!got_decl) _ = try p.expectToken(.semicolon);

// for (init; cond
const cond = try p.expr();
var cond = try p.expr();
if (cond.node != .none) {
try cond.lvalConversion(p);
if (cond.ty.isInt())
try cond.intCast(p, cond.ty.integerPromotion(p.pp.comp))
else if (!cond.ty.isFloat() and cond.ty.specifier != .pointer)
try p.errStr(.statement_scalar, l_paren + 1, try p.typeStr(cond.ty));
}
_ = try p.expectToken(.semicolon);

// for (init; cond; incr
Expand Down Expand Up @@ -2232,7 +2256,7 @@ pub const Result = struct {
}

fn maybeWarnUnused(res: Result, p: *Parser, expr_start: TokenIndex) Error!void {
if (res.ty.specifier == .void) return;
if (res.ty.specifier == .void or res.node == .none) return;
switch (p.nodes.items(.tag)[@enumToInt(res.node)]) {
.invalid, // So that we don't need to check for node == 0
.assign_expr,
Expand Down
2 changes: 1 addition & 1 deletion src/Type.zig
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ qual: Qualifiers = .{},
pub fn isCallable(ty: Type) ?Type {
return switch (ty.specifier) {
.func, .var_args_func, .old_style_func => ty,
.pointer => ty.data.sub_type.isCallable(),
.pointer => if (ty.data.sub_type.isFunc()) ty.data.sub_type.* else null,
else => null,
};
}
Expand Down
12 changes: 12 additions & 0 deletions test/cases/statements.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
void foo(void) {
if ((char)1);
if ((void)1);
switch (1.f);
for (foo;(void)2;);
}


#define EXPECTED_ERRORS "statements.c:3:9: error: statement requires expression with scalar type ('void' invalid)" \
"statements.c:4:13: error: statement requires expression with integer type ('float' invalid)" \
"statements.c:5:10: warning: expression result unused" \
"statements.c:5:10: error: statement requires expression with scalar type ('void' invalid)"

0 comments on commit cee0161

Please sign in to comment.