Skip to content

Commit

Permalink
Parser: validate some uses of constantExpr
Browse files Browse the repository at this point in the history
Previously we wouldn't check for `val == .unavailable` in _Alignas,
case statements and enum values.
  • Loading branch information
Vexu committed Sep 11, 2021
1 parent 4cf6269 commit fddbb25
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 16 deletions.
9 changes: 9 additions & 0 deletions src/Diagnostics.zig
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ pub const Tag = enum {
invalid_typeof,
division_by_zero,
builtin_choose_cond,
alignas_unavailable,
case_val_unavailable,
enum_val_unavailable,
};

const Options = struct {
Expand Down Expand Up @@ -553,6 +556,9 @@ pub fn renderExtra(comp: *Compilation, m: anytype) void {
.invalid_typeof => m.print("'{s} typeof' is invalid", .{msg.extra.str}),
.division_by_zero => m.print("{s} by zero is undefined", .{msg.extra.str}),
.builtin_choose_cond => m.write("'__builtin_choose_expr' requires a constant expression"),
.alignas_unavailable => m.write("'_Alignas' attribute requires integer constant expression"),
.case_val_unavailable => m.write("case value must be an integer constant expression"),
.enum_val_unavailable => m.write("enum value must be an integer constant expression"),
}
m.end(lcs);

Expand Down Expand Up @@ -732,6 +738,9 @@ fn tagKind(diag: *Diagnostics, tag: Tag) Kind {
.arr_init_too_long,
.invalid_typeof,
.builtin_choose_cond,
.alignas_unavailable,
.case_val_unavailable,
.enum_val_unavailable,
=> .@"error",
.to_match_paren,
.to_match_brace,
Expand Down
35 changes: 22 additions & 13 deletions src/Parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -818,23 +818,21 @@ pub const DeclSpec = struct {
}
};

/// typeof
/// : keyword_typeof '(' typeName ')'
/// | keyword_typeof '(' expr ')'
fn typeof(p: *Parser) Error!?Type {
switch (p.tok_ids[p.tok_i]) {
.keyword_typeof, .keyword_typeof1, .keyword_typeof2 => p.tok_i += 1,
else => return null,
}
const l_paren = try p.expectToken(.l_paren);
const start = p.tok_i;
if (try p.typeName()) |ty| {
try p.expectClosing(l_paren, .r_paren);
return ty;
}
p.tok_i = start;
if (p.eatToken(.r_paren)) |r_paren| {
try p.errTok(.expected_expr, r_paren);
return error.ParsingFailed;
}
const typeof_expr = try p.parseNoEval(assignExpr);
const typeof_expr = try p.parseNoEval(expr);
try typeof_expr.expect(p);
try p.expectClosing(l_paren, .r_paren);
return typeof_expr.ty;
}
Expand Down Expand Up @@ -989,6 +987,7 @@ fn initDeclarator(p: *Parser, decl_spec: *DeclSpec) Error!?InitDeclarator {
/// | recordSpec
/// | enumSpec
/// | typedef // IDENTIFIER
/// | typeof
/// atomicTypeSpec : keyword_atomic '(' typeName ')'
/// alignSpec
/// : keyword_alignas '(' typeName ')'
Expand Down Expand Up @@ -1076,7 +1075,10 @@ fn typeSpec(p: *Parser, ty: *Type.Builder) Error!bool {
ty.alignment = inner_ty.alignment;
} else blk: {
const res = try p.constExpr();
if (res.val == .signed and res.val.signed < 0) {
if (res.val == .unavailable) {
try p.errTok(.alignas_unavailable, ty.align_tok.?);
break :blk;
} else if (res.val == .signed and res.val.signed < 0) {
try p.errExtra(.negative_alignment, ty.align_tok.?, .{ .signed = res.val.signed });
break :blk;
}
Expand Down Expand Up @@ -1277,8 +1279,6 @@ fn recordDecls(p: *Parser) Error!void {

/// specQual : (typeSpec | typeQual | alignSpec)+
fn specQual(p: *Parser) Error!?Type {
if (try p.typeof()) |ty| return ty;

var spec: Type.Builder = .{};
if (try p.typeSpec(&spec)) {
if (spec.alignment != 0) try p.errTok(.align_ignored, spec.align_tok.?);
Expand Down Expand Up @@ -1399,7 +1399,12 @@ fn enumerator(p: *Parser) Error!?EnumFieldAndNode {
},
};
if (p.eatToken(.equal)) |_| {
res = try p.constExpr();
const specified = try p.constExpr();
if (specified.val == .unavailable) {
try p.errTok(.enum_val_unavailable, name_tok + 2);
} else {
res = specified;
}
}

if (p.findSymbol(name_tok, .definition)) |scope| switch (scope) {
Expand Down Expand Up @@ -2244,6 +2249,10 @@ fn labeledStmt(p: *Parser) Error!?NodeIndex {
.data = .{ .bin = .{ .lhs = val.node, .rhs = s } },
});
if (p.findSwitch()) |some| {
if (val.val == .unavailable) {
try p.errTok(.case_val_unavailable, case + 1);
return node;
}
const gop = try some.cases.getOrPut(val);
if (gop.found_existing) {
switch (val.val) {
Expand Down Expand Up @@ -3763,7 +3772,7 @@ fn unExpr(p: *Parser) Error!Result {
try p.expectClosing(l_paren, .r_paren);
} else {
p.tok_i = expected_paren;
res = try p.parseNoEval(assignExpr);
res = try p.parseNoEval(unExpr);
}
} else {
res = try p.parseNoEval(unExpr);
Expand Down Expand Up @@ -3792,7 +3801,7 @@ fn unExpr(p: *Parser) Error!Result {
try p.expectClosing(l_paren, .r_paren);
} else {
p.tok_i = expected_paren;
res = try p.parseNoEval(assignExpr);
res = try p.parseNoEval(unExpr);
try p.errTok(.alignof_expr, expected_paren);
}
} else {
Expand Down
3 changes: 3 additions & 0 deletions test/cases/alignment.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ int baz(_Alignas(8) int d) {
_Alignas(536870912) int e;
_Alignas(-2) int f;

_Alignas(c) int x;

#define TESTS_SKIPPED 1
#define EXPECTED_ERRORS "alignment.c:3:3: error: '_Alignas' attribute only applies to variables and fields" \
"alignment.c:4:1: warning: requested alignment of zero is ignored" \
Expand All @@ -21,3 +23,4 @@ _Alignas(-2) int f;
"alignment.c:9:21: warning: '_Alignas' attribute is ignored here" \
"alignment.c:12:1: error: requested alignment of 536870912 is too large" \
"alignment.c:13:1: error: requested negative alignment of -2 is invalid" \
"alignment.c:15:1: error: '_Alignas' attribute requires integer constant expression" \
7 changes: 6 additions & 1 deletion test/cases/containers.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ struct StructWithEnum{
};
};

enum {
F = IntTest,
};

#define EXPECTED_ERRORS "containers.c:15:8: error: use of 'Foo' with tag type that does not match previous definition" \
"containers.c:9:6: note: previous definition is here" \
"containers.c:15:12: error: variable has incomplete type 'struct Foo'" \
Expand All @@ -64,4 +68,5 @@ struct StructWithEnum{
"containers.c:38:5: error: invalid application of 'sizeof' to an incomplete type 'struct Foo'" \
"containers.c:39:5: error: must use 'struct' tag to refer to type 'StructTest'" \
"containers.c:46:1: warning: declaration does not declare anything" \
"containers.c:54:13: error: expression is not an integer constant expression"
"containers.c:54:13: error: expression is not an integer constant expression" \
"containers.c:59:9: error: enum value must be an integer constant expression" \
2 changes: 2 additions & 0 deletions test/cases/sizeof alignof.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ int quuux(void) {
return _Alignof(0/0);
}

_Static_assert(sizeof((void)1, (int*)0) == sizeof(int*), "sizeof");

#define EXPECTED_ERRORS "sizeof alignof.c:10:19: error: expected parentheses around type name" \
"sizeof alignof.c:10:37: error: expected parentheses around type name" \
"sizeof alignof.c:14:32: warning: '_Alignof' applied to an expression is a GNU extension" \
Expand Down
5 changes: 4 additions & 1 deletion test/cases/statements.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ void foo(void) {
if ((void)1);
switch (1.f);
for (foo;(void)2;);
int x;
switch (x) case x: return;
}


#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)"
"statements.c:5:10: error: statement requires expression with scalar type ('void' invalid)" \
"statements.c:7:21: error: case value must be an integer constant expression" \
7 changes: 6 additions & 1 deletion test/cases/typeof.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ void baz(void) {
typeof(0/0) divzero = 0;
}

typeof((void)1, (int*)2) a = 2.f;
typeof();

#define EXPECTED_ERRORS "typeof.c:24:9: warning: incompatible pointer types assigning to 'int *' from incompatible type 'float *'" \
"typeof.c:28:7: error: expression is not assignable" \
"typeof.c:30:7: error: expression is not assignable"
"typeof.c:30:7: error: expression is not assignable" \
"typeof.c:34:30: error: initializing 'int *' from incompatible type 'float'" \
"typeof.c:35:8: error: expected expression" \

0 comments on commit fddbb25

Please sign in to comment.