dusk: basic binary expression errors

This commit is contained in:
Ali Chraghi 2023-04-10 14:40:01 +03:30 committed by Stephen Gutekanst
parent a14cb00ddd
commit e620d8f759
5 changed files with 336 additions and 148 deletions

View file

@ -362,11 +362,6 @@ pub const Node = struct {
/// RHS : -- /// RHS : --
depth_texture_type, depth_texture_type,
/// TOK : ident
/// LHS : --
/// RHS : --
user_type,
// ####### Attribute ####### // ####### Attribute #######
// TOK : attr // TOK : attr

View file

@ -7,7 +7,7 @@ const AstGen = @This();
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
tree: *const Ast, tree: *const Ast,
instructions: std.ArrayListUnmanaged(IR.Inst) = .{}, instructions: IR.Inst.List = .{},
refs: std.ArrayListUnmanaged(IR.Inst.Ref) = .{}, refs: std.ArrayListUnmanaged(IR.Inst.Ref) = .{},
strings: std.ArrayListUnmanaged(u8) = .{}, strings: std.ArrayListUnmanaged(u8) = .{},
scratch: std.ArrayListUnmanaged(IR.Inst.Ref) = .{}, scratch: std.ArrayListUnmanaged(IR.Inst.Ref) = .{},
@ -52,6 +52,21 @@ pub fn genTranslationUnit(self: *AstGen) !u32 {
return try self.addRefList(self.scratch.items[scratch_top..]); return try self.addRefList(self.scratch.items[scratch_top..]);
} }
pub fn genDecl(self: *AstGen, scope: *Scope, node: Ast.Index) !IR.Inst.Ref {
const ref = scope.decls.get(node).?;
if (ref != .none) return ref;
const decl = switch (self.tree.nodeTag(node)) {
.global_variable => try self.genGlobalVariable(scope, node),
.type_alias => try self.genTypeAlias(scope, node),
.struct_decl => try self.genStruct(scope, node),
else => return error.AnalysisFail, // TODO: make this unreachable
};
scope.decls.putAssumeCapacity(node, decl);
return decl;
}
/// adds `decls` to scope and checks for re-declarations
pub fn scanDecls(self: *AstGen, scope: *Scope, decls: []const Ast.Index) !void { pub fn scanDecls(self: *AstGen, scope: *Scope, decls: []const Ast.Index) !void {
std.debug.assert(scope.decls.count() == 0); std.debug.assert(scope.decls.count() == 0);
@ -69,15 +84,15 @@ pub fn scanDecls(self: *AstGen, scope: *Scope, decls: []const Ast.Index) !void {
// ); // );
// } // }
var name_iter = scope.decls.keyIterator(); var iter = scope.decls.keyIterator();
while (name_iter.next()) |node| { while (iter.next()) |node| {
if (std.mem.eql(u8, self.declNameLoc(node.*).?.slice(self.tree.source), name)) { if (std.mem.eql(u8, name, self.declNameLoc(node.*).?.slice(self.tree.source))) {
try self.errors.add( try self.errors.add(
loc, loc,
"redeclaration of '{s}'", "redeclaration of '{s}'",
.{name}, .{name},
try self.errors.createNote( try self.errors.createNote(
self.declNameLoc(node.*).?, self.declNameLoc(node.*),
"other declaration here", "other declaration here",
.{}, .{},
), ),
@ -90,34 +105,19 @@ pub fn scanDecls(self: *AstGen, scope: *Scope, decls: []const Ast.Index) !void {
} }
} }
pub fn genDecl(self: *AstGen, scope: *Scope, node: Ast.Index) !IR.Inst.Ref { /// takes token location and returns the first declaration
const ref = scope.decls.get(node).?; /// in current and parent scopes
if (ref != .none) return ref; pub fn declRef(self: *AstGen, scope: *Scope, loc: Token.Loc) error{ OutOfMemory, AnalysisFail }!IR.Inst.Ref {
const decl = switch (self.tree.nodeTag(node)) {
.global_variable => try self.genGlobalVariable(scope, node),
.type_alias => try self.genTypeAlias(scope, node),
.struct_decl => try self.genStruct(scope, node),
else => return error.AnalysisFail, // TODO: else prong should not ever be trigerred
};
scope.decls.putAssumeCapacity(node, decl);
return decl;
}
pub fn declRef(self: *AstGen, scope: *Scope, loc: Token.Loc) !IR.Inst.Ref {
const name = loc.slice(self.tree.source); const name = loc.slice(self.tree.source);
var s = scope; var s = scope;
while (true) { while (true) {
var name_iter = s.decls.keyIterator(); var node_iter = s.decls.keyIterator();
while (name_iter.next()) |node| { while (node_iter.next()) |node| {
if (std.mem.eql(u8, self.declNameLoc(node.*).?.slice(self.tree.source), name)) { if (std.mem.eql(u8, name, self.declNameLoc(node.*).?.slice(self.tree.source))) {
return self.genDecl(scope, node.*); return self.genDecl(scope, node.*);
} }
} }
s = scope.parent orelse break; s = scope.parent orelse {
}
try self.errors.add( try self.errors.add(
loc, loc,
"use of undeclared identifier '{s}'", "use of undeclared identifier '{s}'",
@ -125,6 +125,22 @@ pub fn declRef(self: *AstGen, scope: *Scope, loc: Token.Loc) !IR.Inst.Ref {
null, null,
); );
return error.AnalysisFail; return error.AnalysisFail;
};
}
}
/// returns declaration type or if type is unkown, returns value
pub fn valueOrType(inst: IR.Inst) IR.Inst.Ref {
switch (inst.tag) {
.global_variable_decl => {
const decl_type = inst.data.global_variable_decl.type;
if (decl_type == .none) {
return inst.data.global_variable_decl.expr;
}
return decl_type;
},
else => unreachable,
}
} }
pub fn genTypeAlias(self: *AstGen, scope: *Scope, node: Ast.Index) !IR.Inst.Ref { pub fn genTypeAlias(self: *AstGen, scope: *Scope, node: Ast.Index) !IR.Inst.Ref {
@ -286,47 +302,103 @@ pub fn genStruct(self: *AstGen, scope: *Scope, node: Ast.Index) !IR.Inst.Ref {
} }
pub fn genExpr(self: *AstGen, scope: *Scope, node: Ast.Index) !IR.Inst.Ref { pub fn genExpr(self: *AstGen, scope: *Scope, node: Ast.Index) !IR.Inst.Ref {
const tag = self.tree.nodeTag(node); const node_loc = self.tree.tokenLoc(self.tree.nodeToken(node));
const lhs = self.tree.nodeLHS(node); const node_tag = self.tree.nodeTag(node);
const rhs = self.tree.nodeRHS(node); const node_lhs = self.tree.nodeLHS(node);
const node_rhs = self.tree.nodeRHS(node);
const node_lhs_loc = self.tree.tokenLoc(self.tree.nodeToken(node_lhs));
const node_rhs_loc = self.tree.tokenLoc(self.tree.nodeToken(node_rhs));
switch (tag) { switch (node_tag) {
.bool_true => return .true_literal, .bool_true => return .true_literal,
.bool_false => return .true_literal, .bool_false => return .false_literal,
else => {}, else => {},
} }
const inst_index = try self.reserveInst(); const inst_index = try self.reserveInst();
const inst: IR.Inst = switch (tag) { const inst: IR.Inst = switch (node_tag) {
.number_literal => .{ .tag = .integer_literal, .data = .{ .integer_literal = 1 } }, .number_literal => .{ .tag = .integer_literal, .data = .{ .integer_literal = 1 } },
.not => .{ .tag = .not, .data = .{ .ref = try self.genExpr(scope, lhs) } }, .not => .{ .tag = .not, .data = .{ .ref = try self.genExpr(scope, node_lhs) } },
.negate => .{ .tag = .negate, .data = .{ .ref = try self.genExpr(scope, lhs) } }, .negate => .{ .tag = .negate, .data = .{ .ref = try self.genExpr(scope, node_lhs) } },
.deref => .{ .tag = .deref, .data = .{ .ref = try self.genExpr(scope, lhs) } }, .deref => .{ .tag = .deref, .data = .{ .ref = try self.genExpr(scope, node_lhs) } },
.addr_of => .{ .tag = .addr_of, .data = .{ .ref = try self.genExpr(scope, lhs) } }, .addr_of => .{ .tag = .addr_of, .data = .{ .ref = try self.genExpr(scope, node_lhs) } },
.mul => .{ .tag = .mul, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } },
.div => .{ .tag = .div, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .mul,
.mod => .{ .tag = .mod, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .div,
.add => .{ .tag = .add, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .mod,
.sub => .{ .tag = .sub, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .add,
.shift_left => .{ .tag = .shift_left, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .sub,
.shift_right => .{ .tag = .shift_right, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .shift_left,
.binary_and => .{ .tag = .binary_and, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .shift_right,
.binary_or => .{ .tag = .binary_or, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .binary_and,
.binary_xor => .{ .tag = .binary_xor, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .binary_or,
.circuit_and => .{ .tag = .circuit_and, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .binary_xor,
.circuit_or => .{ .tag = .circuit_or, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .circuit_and,
.equal => .{ .tag = .equal, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .circuit_or,
.not_equal => .{ .tag = .not_equal, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .equal,
.less => .{ .tag = .less, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .not_equal,
.less_equal => .{ .tag = .less_equal, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .less,
.greater => .{ .tag = .greater, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .less_equal,
.greater_equal => .{ .tag = .greater_equal, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .greater,
.index_access => .{ .tag = .index, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, .greater_equal,
.component_access => .{ .tag = .member_access, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, => blk: {
.bitcast => .{ .tag = .bitcast, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genType(scope, rhs) } } }, const lhs = try self.genExpr(scope, node_lhs);
const rhs = try self.genExpr(scope, node_rhs);
const inst_tag: IR.Inst.Tag = switch (node_tag) {
.mul => .mul,
.div => .div,
.mod => .mod,
.add => .add,
.sub => .sub,
.shift_left => .shift_left,
.shift_right => .shift_right,
.binary_and => .binary_and,
.binary_or => .binary_or,
.binary_xor => .binary_xor,
.circuit_and => .circuit_and,
.circuit_or => .circuit_or,
.equal => .equal,
.not_equal => .not_equal,
.less => .less,
.less_equal => .less_equal,
.greater => .greater,
.greater_equal => .greater_equal,
else => unreachable,
};
if (try self.isIntegerResulting(lhs, false)) {
if (try self.isIntegerResulting(rhs, false)) {
break :blk .{
.tag = inst_tag,
.data = .{ .binary = .{ .lhs = lhs, .rhs = rhs } },
};
}
}
try self.errors.add(
node_loc,
"incompatible types: '{s}' and '{s}'",
.{ node_lhs_loc.slice(self.tree.source), node_rhs_loc.slice(self.tree.source) },
try self.errors.createNote(
null,
"{} and {}",
.{ self.instructions.items[lhs.toIndex().?].tag, self.instructions.items[rhs.toIndex().?].tag },
),
);
return error.AnalysisFail;
},
.index_access => .{ .tag = .index, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, node_lhs), .rhs = try self.genExpr(scope, node_rhs) } } },
.component_access => .{ .tag = .member_access, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, node_lhs), .rhs = try self.genExpr(scope, node_rhs) } } },
.bitcast => .{ .tag = .bitcast, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, node_lhs), .rhs = try self.genType(scope, node_rhs) } } },
.ident_expr => .{ .ident_expr => .{
.tag = .ident, .tag = .var_ref,
.data = .{ .name = try self.addString(self.tree.tokenLoc(self.tree.nodeToken(node)).slice(self.tree.source)) }, .data = .{
.var_ref = .{
.name = try self.addString(self.tree.tokenLoc(self.tree.nodeToken(node)).slice(self.tree.source)),
.variable = try self.declRef(scope, node_loc),
},
},
}, },
else => { else => {
std.debug.print("WTF REALLY\n", .{}); std.debug.print("WTF REALLY\n", .{});
@ -364,76 +436,6 @@ pub fn addInst(self: *AstGen, inst: IR.Inst) error{OutOfMemory}!IR.Inst.Index {
return @intCast(IR.Inst.Index, self.instructions.items.len - 1); return @intCast(IR.Inst.Index, self.instructions.items.len - 1);
} }
// // pub fn expression(self: *AstGen, node: Ast.Index) !?IR.Expression {
// // const lhs = self.tree.nodeLHS(node);
// // const rhs = self.tree.nodeRHS(node);
// // const loc = self.tree.tokenLoc(self.tree.nodeToken(node));
// // return switch (self.tree.nodeTag(node)) {
// // .mul => {
// // const lir = try self.expression(lhs) orelse return null;
// // const rir = try self.expression(rhs) orelse return null;
// // const is_valid_op =
// // (lir == .number and rir == .number) or
// // ((lir == .construct and lir.construct == .vector) and rir == .number) or
// // (lir == .number and (rir == .construct and rir.construct == .vector)) or
// // ((lir == .construct and lir.construct == .vector) and (rir == .construct and rir.construct == .vector)) or
// // ((lir == .construct and lir.construct == .matrix) and (rir == .construct and rir.construct == .matrix));
// // if (!is_valid_op) {
// // try self.errors.add(
// // loc,
// // "invalid operation with '{s}' and '{s}'",
// // .{ @tagName(std.meta.activeTag(lir)), @tagName(std.meta.activeTag(rir)) },
// // null,
// // );
// // return null;
// // }
// // },
// // // .div,
// // // .mod,
// // // .add,
// // // .sub,
// // // .shift_left,
// // // .shift_right,
// // // .binary_and,
// // // .binary_or,
// // // .binary_xor,
// // // .circuit_and,
// // // .circuit_or,
// // .number_literal => .{ .literal = try self.create(.{ .number = try self.create(try self.numberLiteral(node) orelse return null) }) },
// // .bool_literal => .{ .literal = try self.create(.{ .bool = self.boolLiteral(node) }) },
// // else => return null, // TODO
// // };
// // }
// // pub fn numberLiteral(self: *AstGen, node: Ast.Index) !?IR.NumberLiteral {
// // const loc = self.tree.tokenLoc(self.tree.nodeToken(node));
// // const str = loc.slice(self.tree.source);
// // if (std.mem.startsWith(u8, str, "0") and
// // !std.mem.endsWith(u8, str, "i") and
// // !std.mem.endsWith(u8, str, "u") and
// // !std.mem.endsWith(u8, str, "f") and
// // !std.mem.endsWith(u8, str, "h"))
// // {
// // try self.errors.add(
// // loc,
// // "number literal cannot have leading 0",
// // .{str},
// // null,
// // );
// // return null;
// // }
// // return null;
// // }
// // pub fn boolLiteral(self: *AstGen, node: Ast.Index) bool {
// // const loc = self.tree.tokenLoc(self.tree.nodeToken(node));
// // const str = loc.slice(self.tree.source);
// // return str[0] == 't';
// // }
pub fn genType(self: *AstGen, scope: *Scope, node: Ast.Index) error{ AnalysisFail, OutOfMemory }!IR.Inst.Ref { pub fn genType(self: *AstGen, scope: *Scope, node: Ast.Index) error{ AnalysisFail, OutOfMemory }!IR.Inst.Ref {
return switch (self.tree.nodeTag(node)) { return switch (self.tree.nodeTag(node)) {
.bool_type => try self.genBoolType(node), .bool_type => try self.genBoolType(node),
@ -442,7 +444,7 @@ pub fn genType(self: *AstGen, scope: *Scope, node: Ast.Index) error{ AnalysisFai
.matrix_type => try self.genMatrixType(scope, node), .matrix_type => try self.genMatrixType(scope, node),
.atomic_type => try self.genAtomicType(scope, node), .atomic_type => try self.genAtomicType(scope, node),
.array_type => try self.genArrayType(scope, node), .array_type => try self.genArrayType(scope, node),
.user_type => { .ident_expr => {
const node_loc = self.tree.tokenLoc(self.tree.nodeToken(node)); const node_loc = self.tree.tokenLoc(self.tree.nodeToken(node));
const decl_ref = try self.declRef(scope, node_loc); const decl_ref = try self.declRef(scope, node_loc);
switch (decl_ref) { switch (decl_ref) {
@ -1084,3 +1086,34 @@ pub fn declNameLoc(self: *AstGen, node: Ast.Index) ?Token.Loc {
}; };
return self.tree.tokenLoc(token); return self.tree.tokenLoc(token);
} }
pub fn isIntegerResulting(self: *AstGen, ref: IR.Inst.Ref, is_decl: bool) !bool {
if (is_decl and ref.isNumberType()) {
return true;
}
if (ref.isNumberLiteral(self.instructions) or
ref.is(self.instructions, &.{
.mul,
.div,
.mod,
.add,
.sub,
.shift_left,
.shift_right,
.binary_and,
.binary_or,
.binary_xor,
})) {
return true;
} else if (ref.is(self.instructions, &.{.var_ref})) {
const inst = self.instructions.items[ref.toIndex().?];
const var_inst = self.instructions.items[inst.data.var_ref.variable.toIndex().?];
return self.isIntegerResulting(valueOrType(var_inst), true);
} else if (ref.is(self.instructions, &.{.deref})) {
const inst = self.instructions.items[ref.toIndex().?];
return self.isIntegerResulting(inst.data.ref, true);
}
return false;
}

View file

@ -56,6 +56,7 @@ pub const Inst = struct {
tag: Tag, tag: Tag,
data: Data, data: Data,
pub const List = std.ArrayListUnmanaged(Inst);
pub const Index = u32; pub const Index = u32;
const ref_start_index = @typeInfo(Ref).Enum.fields.len; const ref_start_index = @typeInfo(Ref).Enum.fields.len;
@ -88,6 +89,149 @@ pub const Inst = struct {
return null; return null;
} }
} }
pub fn is(self: Ref, list: List, comptime expected: []const Inst.Tag) bool {
inline for (expected) |e| {
if (list.items[self.toIndex().?].tag == e) return true;
}
return false;
}
pub fn isType(self: Ref, list: List) bool {
return switch (self) {
.none,
.true_literal,
.false_literal,
=> false,
.bool_type,
.i32_type,
.u32_type,
.f32_type,
.f16_type,
.sampler_type,
.comparison_sampler_type,
.external_sampled_texture_type,
=> true,
_ => switch (list.items[self.toIndex().?].tag) {
.struct_decl,
.vector_type,
.matrix_type,
.atomic_type,
.array_type,
.ptr_type,
.sampled_texture_type,
.multisampled_texture_type,
.storage_texture_type,
.depth_texture_type,
=> true,
else => false,
},
};
}
pub fn isNumberType(self: Ref) bool {
return switch (self) {
.i32_type,
.u32_type,
.f32_type,
.f16_type,
=> true,
else => false,
};
}
pub fn isLiteral(self: Ref, list: List) bool {
return switch (self) {
.true_literal,
.false_literal,
=> true,
.none,
.bool_type,
.i32_type,
.u32_type,
.f32_type,
.f16_type,
.sampler_type,
.comparison_sampler_type,
.external_sampled_texture_type,
=> false,
_ => switch (list.items[self.toIndex().?].tag) {
.integer_literal,
.float_literal,
=> true,
else => false,
},
};
}
pub fn isBoolLiteral(self: Ref) bool {
return switch (self) {
.true_literal,
.false_literal,
=> true,
else => false,
};
}
pub fn isNumberLiteral(self: Ref, list: List) bool {
const i = self.toIndex() orelse return false;
return switch (list.items[i].tag) {
.integer_literal,
.float_literal,
=> true,
else => false,
};
}
pub fn isExpr(self: Ref, list: List) bool {
const i = self.toIndex() orelse return false;
return switch (list.items[i].tag) {
.index,
.member_access,
.bitcast,
.ident,
=> true,
else => self.isBinaryExpr() or self.isUnaryExpr(),
};
}
pub fn isBinaryExpr(self: Ref, list: List) bool {
const i = self.toIndex() orelse return false;
return switch (list.items[i].tag) {
.mul,
.div,
.mod,
.add,
.sub,
.shift_left,
.shift_right,
.binary_and,
.binary_or,
.binary_xor,
.circuit_and,
.circuit_or,
.equal,
.not_equal,
.less,
.less_equal,
.greater,
.greater_equal,
=> true,
else => false,
};
}
pub fn isUnaryExpr(self: Ref, list: List) bool {
const i = self.toIndex() orelse return false;
return switch (list.items[i].tag) {
.not,
.negate,
.deref,
.addr_of,
=> true,
else => false,
};
}
}; };
pub const Tag = enum(u6) { pub const Tag = enum(u6) {
@ -187,8 +331,8 @@ pub const Inst = struct {
/// data is binary (lhs is expr, rhs is type) /// data is binary (lhs is expr, rhs is type)
bitcast, bitcast,
/// data is name /// data is var_ref
ident, var_ref,
pub fn isDecl(self: Tag) bool { pub fn isDecl(self: Tag) bool {
return switch (self) { return switch (self) {
@ -200,8 +344,7 @@ pub const Inst = struct {
pub const Data = union { pub const Data = union {
ref: Ref, ref: Ref,
/// index to null-terminated string in `strings` var_ref: VarRef,
name: u32,
global_variable_decl: GlobalVariableDecl, global_variable_decl: GlobalVariableDecl,
struct_decl: StructDecl, struct_decl: StructDecl,
struct_member: StructMember, struct_member: StructMember,
@ -231,10 +374,16 @@ pub const Inst = struct {
member_access: MemberAccess, member_access: MemberAccess,
}; };
pub const VarRef = struct {
/// index to null-terminated string in `strings`
name: u32,
variable: Ref,
};
pub const GlobalVariableDecl = struct { pub const GlobalVariableDecl = struct {
/// index to null-terminated string in `strings` /// index to null-terminated string in `strings`
name: u32, name: u32,
type: Ref, type: Ref = .none,
addr_space: AddressSpace, addr_space: AddressSpace,
access_mode: AccessMode, access_mode: AccessMode,
/// length of attributes /// length of attributes

View file

@ -305,6 +305,16 @@ pub fn globalVarDecl(p: *Parser, attrs: ?Ast.Index) !?Ast.Index {
}; };
} }
if (initializer == Ast.null_index and var_type == Ast.null_index) {
try p.errors.add(
p.getToken(.loc, var_token),
"initializer expression is required while type is unknown",
.{},
null,
);
return error.Parsing;
}
const extra = try p.addExtra(Ast.Node.GlobalVarDecl{ const extra = try p.addExtra(Ast.Node.GlobalVarDecl{
.attrs = attrs orelse Ast.null_index, .attrs = attrs orelse Ast.null_index,
.name = name_token, .name = name_token,
@ -1042,7 +1052,7 @@ pub fn expectTypeSpecifier(p: *Parser) error{ OutOfMemory, Parsing }!Ast.Index {
pub fn typeSpecifier(p: *Parser) !?Ast.Index { pub fn typeSpecifier(p: *Parser) !?Ast.Index {
if (p.peekToken(.tag, 0) == .ident) { if (p.peekToken(.tag, 0) == .ident) {
const main_token = p.advanceToken(); const main_token = p.advanceToken();
return try p.addNode(.{ .tag = .user_type, .main_token = main_token }); return try p.addNode(.{ .tag = .ident_expr, .main_token = main_token });
} }
return p.typeSpecifierWithoutIdent(); return p.typeSpecifierWithoutIdent();
} }

View file

@ -19,7 +19,8 @@ struct SimParams {
rule3Scale : f32, rule3Scale : f32,
} }
@group(0) @binding(0) var<uniform> params : SimParams; var v0 = 5;
@group(0) @binding(0) var<uniform> params : SimParams = *v0 * 1 + 2;
@group(0) @binding(1) var<storage> particlesSrc : Particles; @group(0) @binding(1) var<storage> particlesSrc : Particles;
@group(0) @binding(2) var<storage,read_write> particlesDst : Particles; @group(0) @binding(2) var<storage,read_write> particlesDst : Particles;