Skip to content

Commit

Permalink
Nested diagnostics (#384)
Browse files Browse the repository at this point in the history
  • Loading branch information
jackalcooper authored Dec 31, 2024
1 parent 0086ac1 commit 6d4a4e6
Show file tree
Hide file tree
Showing 15 changed files with 106 additions and 98 deletions.
5 changes: 1 addition & 4 deletions lib/beaver/composer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,7 @@ defmodule Beaver.Composer do

{:error, diagnostics} ->
raise ArgumentError,
(for {_severity, loc, d, _num} <- diagnostics,
reduce: "Unexpected failure running passes" do
acc -> "#{acc}\n#{to_string(loc)}: #{d}"
end)
MLIR.Diagnostic.format(diagnostics, "Unexpected failure running passes")
end
end

Expand Down
14 changes: 2 additions & 12 deletions lib/beaver/mlir.ex
Original file line number Diff line number Diff line change
Expand Up @@ -232,12 +232,7 @@ defmodule Beaver.MLIR do
raise "MLIR operation verification failed because the operation is null. Maybe it is parsed from an ill-formed text format? Please have a look at the diagnostic output above by MLIR C++"

{:error, diagnostics} ->
diagnostics =
for {_, loc, d, _} <- diagnostics, reduce: "" do
acc -> "#{acc}\n#{__MODULE__.to_string(loc)}: #{d}"
end

raise "MLIR operation verification failed, #{diagnostics}"
raise MLIR.Diagnostic.format(diagnostics, "MLIR operation verification failed")
end
end

Expand Down Expand Up @@ -316,12 +311,7 @@ defmodule Beaver.MLIR do
if MLIR.LogicalResult.success?(result) do
{:ok, op}
else
diagnostics =
for {_, loc, d, _} <- diagnostics, reduce: "" do
acc -> "#{acc}\n#{__MODULE__.to_string(loc)}: #{d}"
end

{:error, "failed to apply pattern set.\n#{diagnostics}"}
{:error, MLIR.Diagnostic.format(diagnostics, "failed to apply pattern set.")}
end
end
end
Expand Down
6 changes: 1 addition & 5 deletions lib/beaver/mlir/attribute.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ defmodule Beaver.MLIR.Attribute do
raise ArgumentError, "fail to parse attribute: #{attr_str}"

diagnostics when is_list(diagnostics) ->
raise ArgumentError,
(for {_severity, loc, d, _num} <- diagnostics,
reduce: "fail to parse attribute" do
acc -> "#{acc}\n#{to_string(loc)}: #{d}"
end)
raise ArgumentError, MLIR.Diagnostic.format(diagnostics, "fail to parse attribute")
end
end

Expand Down
27 changes: 27 additions & 0 deletions lib/beaver/mlir/diagnostic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,31 @@ defmodule Beaver.MLIR.Diagnostic do
end

defdelegate detach(ctx, handler_id), to: MLIR.CAPI, as: :mlirContextDetachDiagnosticHandler

def walk({_, _, _, []} = diagnostic, acc, fun) do
fun.(diagnostic, acc)
end

def walk({_, _, _, nested} = diagnostic, acc, fun) do
acc = fun.(diagnostic, acc)

Enum.reduce(nested, acc, fn d, acc ->
walk(d, acc, fun)
end)
end

def format(diagnostics, prefix \\ "") do
{str, _} =
for d <- diagnostics,
reduce: {prefix, 0} do
acc ->
MLIR.Diagnostic.walk(d, acc, fn {_, loc, d, _}, {str, level} ->
prefix = String.duplicate(" ", level * 2)
d = String.replace(d, ~r"\n", "\n#{prefix} ")
{"#{str}\n#{prefix}at #{loc}: #{d}", level + 1}
end)
end

str
end
end
7 changes: 1 addition & 6 deletions lib/beaver/mlir/execution_engine.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,7 @@ defmodule Beaver.MLIR.ExecutionEngine do
)

if MLIR.null?(jit) do
diagnostics =
for {_, loc, d, _} <- diagnostics, reduce: "Execution engine creation failed" do
acc -> "#{acc}\n#{to_string(loc)}: #{d}"
end

raise ArgumentError, diagnostics
raise ArgumentError, MLIR.Diagnostic.format(diagnostics, "Execution engine creation failed")
end

jit
Expand Down
6 changes: 1 addition & 5 deletions lib/beaver/mlir/module.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,7 @@ defmodule Beaver.MLIR.Module do
raise ArgumentError, "calling a bang function to parse module must be eager"

{:error, diagnostics} ->
raise ArgumentError,
(for {_severity, loc, d, _num} <- diagnostics,
reduce: "fail to parse module" do
acc -> "#{acc}\n#{to_string(loc)}: #{d}"
end)
raise ArgumentError, MLIR.Diagnostic.format(diagnostics, "fail to parse module")

{:ok, module} ->
MLIR.verify!(module)
Expand Down
11 changes: 6 additions & 5 deletions lib/beaver/native.ex
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ defmodule Beaver.Native do
forward(mod, :primitive, [ref])
end

defp postprocess_diagnostics({severity_i, loc_ref, note, nested}) do
{Beaver.MLIR.Diagnostic.severity(severity_i), %Beaver.MLIR.Location{ref: loc_ref},
to_string(note), Enum.map(nested, &postprocess_diagnostics/1)}
end

def check!(ret) do
case ret do
{:kind, mod, ref} when is_atom(mod) and is_reference(ref) ->
Expand All @@ -76,11 +81,7 @@ defmodule Beaver.Native do
struct!(mod, %{ref: ref})
rescue
UndefinedFunctionError -> ref
end,
for {severity_i, loc_ref, note, num} <- diagnostics do
{Beaver.MLIR.Diagnostic.severity(severity_i), %Beaver.MLIR.Location{ref: loc_ref},
to_string(note), num}
end}
end, Enum.map(diagnostics, &postprocess_diagnostics/1)}

ret ->
ret
Expand Down
6 changes: 3 additions & 3 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
%{
"benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"credo": {:hex, :credo, "1.7.10", "6e64fe59be8da5e30a1b96273b247b5cf1cc9e336b5fd66302a64b25749ad44d", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "71fbc9a6b8be21d993deca85bf151df023a3097b01e09a2809d460348561d8cd"},
"credo": {:hex, :credo, "1.7.11", "d3e805f7ddf6c9c854fd36f089649d7cf6ba74c42bc3795d587814e3c9847102", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "56826b4306843253a66e47ae45e98e7d284ee1f95d53d1612bb483f88a8cf219"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"},
"earmark_parser": {:hex, :earmark_parser, "1.4.42", "f23d856f41919f17cd06a493923a722d87a2d684f143a1e663c04a2b93100682", [:mix], [], "hexpm", "6915b6ca369b5f7346636a2f41c6a6d78b5af419d61a611079189233358b8b8b"},
"elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
"ex_doc": {:hex, :ex_doc, "0.35.1", "de804c590d3df2d9d5b8aec77d758b00c814b356119b3d4455e4b8a8687aecaf", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2121c6402c8d44b05622677b761371a759143b958c6c19f6558ff64d0aed40df"},
"ex_doc": {:hex, :ex_doc, "0.36.1", "4197d034f93e0b89ec79fac56e226107824adcce8d2dd0a26f5ed3a95efc36b1", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d7d26a7cf965dacadcd48f9fa7b5953d7d0cfa3b44fa7a65514427da44eafd89"},
"file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"kinda": {:hex, :kinda, "0.9.4", "007e25491bcd3af8a95e0179d9044362dc336920bbe5dbd6515196a5e938b201", [:mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "a5ec71839edf88e52f3e68a2c2210b9a9a00ee414ff369e9801b1184b3844447"},
Expand Down
58 changes: 30 additions & 28 deletions native/src/diagnostic.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,53 +8,55 @@ const e = @import("erl_nif");
const MutexToken = @import("logical_mutex.zig").Token;
const kinda = @import("kinda");
const result = @import("kinda").result;
const StringRefCollector = @import("string_ref.zig").StringRefCollector;
const PrinterNIF = @import("string_ref.zig").PrinterNIF;

// collect diagnostic as {severity, loc, message, num_notes}
// collect diagnostic as {severity, loc, message, nested_notes}
const DiagnosticAggregator = struct {
const Container = std.ArrayList(beam.term);
env: beam.env,
container: Container = undefined,
fn collectDiagnostic(diagnostic: c.MlirDiagnostic, userData: ?*@This()) !mlir_capi.LogicalResult.T {
fn collectDiagnosticNested(diagnostic: c.MlirDiagnostic, userData: ?*@This()) !beam.term {
const env = userData.?.env;
var note_col = StringRefCollector.init(env);
c.mlirDiagnosticPrint(diagnostic, StringRefCollector.append, @constCast(@ptrCast(@alignCast(&note_col))));
const note = try note_col.collect_and_destroy();
var entry_slice: []beam.term = try beam.allocator.alloc(beam.term, 4);
entry_slice[0] = try beam.make(i64, env, c.mlirDiagnosticGetSeverity(diagnostic));
entry_slice[1] = try mlir_capi.Location.resource.make(env, c.mlirDiagnosticGetLocation(diagnostic));
entry_slice[2] = note;
const severity = c.mlirDiagnosticGetSeverity(diagnostic);
const location = try mlir_capi.Location.resource.make(env, c.mlirDiagnosticGetLocation(diagnostic));
const note = try PrinterNIF(mlir_capi.Diagnostic, c.mlirDiagnosticPrint).print_make(env, diagnostic);

const num_notes: usize = @intCast(c.mlirDiagnosticGetNumNotes(diagnostic));
entry_slice[3] = try beam.make(usize, env, num_notes);
try userData.?.container.append(beam.make_tuple(env, entry_slice));
defer beam.allocator.free(entry_slice);
if (num_notes > 0) {
const nested: []c.MlirDiagnostic = try beam.allocator.alloc(c.MlirDiagnostic, num_notes);
defer beam.allocator.free(nested);
for (0..num_notes) |i| {
const nested_diagnostic = c.mlirDiagnosticGetNote(diagnostic, @intCast(i));
if (c.beaverLogicalResultIsFailure(try collectDiagnostic(nested_diagnostic, userData))) {
return c.mlirLogicalResultFailure();
}
}
var nested_notes: []beam.term = try beam.allocator.alloc(beam.term, num_notes);
defer beam.allocator.free(nested_notes);
for (0..num_notes) |i| {
const nested_diagnostic = c.mlirDiagnosticGetNote(diagnostic, @intCast(i));
nested_notes[i] = try collectDiagnosticNested(nested_diagnostic, userData);
}

var entry_slice: []beam.term = try beam.allocator.alloc(beam.term, 4);
defer beam.allocator.free(entry_slice);
entry_slice[0] = try beam.make(i64, env, severity);
entry_slice[1] = location;
entry_slice[2] = note;
entry_slice[3] = beam.make_term_list(env, nested_notes);
return beam.make_tuple(env, entry_slice);
}

fn collectDiagnosticTopLevel(diagnostic: c.MlirDiagnostic, userData: ?*@This()) !mlir_capi.LogicalResult.T {
try userData.?.container.append(try collectDiagnosticNested(diagnostic, userData));
return c.mlirLogicalResultSuccess();
}
fn errorHandler(diagnostic: c.MlirDiagnostic, userData: ?*anyopaque) callconv(.C) mlir_capi.LogicalResult.T {
return collectDiagnostic(diagnostic, @ptrCast(@alignCast(userData))) catch return c.mlirLogicalResultFailure();
return collectDiagnosticTopLevel(diagnostic, @ptrCast(@alignCast(userData))) catch return c.mlirLogicalResultFailure();
}
fn deleteUserData(userData: ?*anyopaque) callconv(.C) void {
const ud: ?*@This() = @ptrCast(@alignCast(userData));
beam.allocator.destroy(ud.?);
const this: ?*@This() = @ptrCast(@alignCast(userData));
this.?.container.deinit();
beam.allocator.destroy(this.?);
}
fn init(env: beam.env) !*@This() {
var userData = try beam.allocator.create(DiagnosticAggregator);
userData.env = env;
userData.container = Container.init(beam.allocator);
return userData;
}
fn collect_and_destroy(this: *@This()) !beam.term {
defer this.container.deinit();
fn to_list(this: *@This()) !beam.term {
return beam.make_term_list(this.env, this.container.items);
}
};
Expand All @@ -65,7 +67,7 @@ pub fn call_with_diagnostics(env: beam.env, ctx: mlir_capi.Context.T, f: anytype
defer c.mlirContextDetachDiagnosticHandler(ctx, id);
var res_slice: []beam.term = try beam.allocator.alloc(beam.term, 2);
res_slice[0] = try @call(.auto, f, args);
res_slice[1] = try DiagnosticAggregator.collect_and_destroy(userData);
res_slice[1] = try DiagnosticAggregator.to_list(userData);
defer beam.allocator.free(res_slice);
return beam.make_tuple(env, res_slice);
}
Expand Down
1 change: 0 additions & 1 deletion native/src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const beam = @import("beam");
const kinda = @import("kinda");
const e = @import("erl_nif");
const mlir_capi = @import("mlir_capi.zig");
pub const c = @import("prelude.zig");
const enif_support = @import("enif_support.zig");
const diagnostic = @import("diagnostic.zig");
const pass = @import("pass.zig");
Expand Down
37 changes: 21 additions & 16 deletions native/src/string_ref.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub const c = @import("prelude.zig");
const mem = @import("std").mem;

const print_nif_prefix = "beaver_raw_to_string_";
pub fn PrinterNIF(comptime name: []const u8, comptime ResourceKind: type, comptime print_fn: anytype) type {
pub fn PrinterNIF(comptime ResourceKind: type, comptime print_fn: anytype) type {
return struct {
const Error = error{
NullPointerFound,
Expand All @@ -19,8 +19,7 @@ pub fn PrinterNIF(comptime name: []const u8, comptime ResourceKind: type, compti
const printer: *@This() = @ptrCast(@alignCast(userData));
printer.*.buffer.appendSlice(s.data[0..s.length]) catch unreachable;
}
fn to_string(env: beam.env, _: c_int, args: [*c]const beam.term) !beam.term {
const entity: ResourceKind.T = try ResourceKind.resource.fetch(env, args[0]);
pub fn print_make(env: beam.env, entity: ResourceKind.T) !beam.term {
if (entity.ptr == null) {
return Error.NullPointerFound;
}
Expand All @@ -29,7 +28,13 @@ pub fn PrinterNIF(comptime name: []const u8, comptime ResourceKind: type, compti
print_fn(entity, collect_string_ref, &printer);
return beam.make_slice(env, printer.buffer.items);
}
const entry = result.nif(print_nif_prefix ++ name, 1, to_string).entry;
fn to_string(env: beam.env, _: c_int, args: [*c]const beam.term) !beam.term {
const entity: ResourceKind.T = try ResourceKind.resource.fetch(env, args[0]);
return print_make(env, entity);
}
fn nif(comptime name: []const u8) e.ErlNifFunc {
return result.nif(print_nif_prefix ++ name, 1, to_string).entry;
}
};
}

Expand Down Expand Up @@ -130,16 +135,16 @@ fn beaver_raw_get_string_ref(env: beam.env, _: c_int, args: [*c]const beam.term)
pub const nifs = .{
result.nif("beaver_raw_get_string_ref", 1, beaver_raw_get_string_ref).entry,
result.nif(print_nif_prefix ++ "StringRef", 1, beaver_raw_string_ref_to_binary).entry,
PrinterNIF("Attribute", mlir_capi.Attribute, c.mlirAttributePrint).entry,
PrinterNIF("Type", mlir_capi.Type, c.mlirTypePrint).entry,
PrinterNIF("Operation", mlir_capi.Operation, c.mlirOperationPrint).entry,
PrinterNIF("OperationSpecialized", mlir_capi.Operation, c.beaverOperationPrintSpecializedFrom).entry,
PrinterNIF("OperationGeneric", mlir_capi.Operation, c.beaverOperationPrintGenericOpForm).entry,
PrinterNIF("OperationBytecode", mlir_capi.Operation, c.mlirOperationWriteBytecode).entry,
PrinterNIF("Value", mlir_capi.Value, c.mlirValuePrint).entry,
PrinterNIF("OpPassManager", mlir_capi.OpPassManager, c.mlirPrintPassPipeline).entry,
PrinterNIF("AffineMap", mlir_capi.AffineMap, c.mlirAffineMapPrint).entry,
PrinterNIF("Location", mlir_capi.Location, c.beaverLocationPrint).entry,
PrinterNIF("Identifier", mlir_capi.Identifier, c.mlirIdentifierPrint).entry,
PrinterNIF("Diagnostic", mlir_capi.Diagnostic, c.mlirDiagnosticPrint).entry,
PrinterNIF(mlir_capi.Attribute, c.mlirAttributePrint).nif("Attribute"),
PrinterNIF(mlir_capi.Type, c.mlirTypePrint).nif("Type"),
PrinterNIF(mlir_capi.Operation, c.mlirOperationPrint).nif("Operation"),
PrinterNIF(mlir_capi.Operation, c.beaverOperationPrintSpecializedFrom).nif("OperationSpecialized"),
PrinterNIF(mlir_capi.Operation, c.beaverOperationPrintGenericOpForm).nif("OperationGeneric"),
PrinterNIF(mlir_capi.Operation, c.mlirOperationWriteBytecode).nif("OperationBytecode"),
PrinterNIF(mlir_capi.Value, c.mlirValuePrint).nif("Value"),
PrinterNIF(mlir_capi.OpPassManager, c.mlirPrintPassPipeline).nif("OpPassManager"),
PrinterNIF(mlir_capi.AffineMap, c.mlirAffineMapPrint).nif("AffineMap"),
PrinterNIF(mlir_capi.Location, c.beaverLocationPrint).nif("Location"),
PrinterNIF(mlir_capi.Identifier, c.mlirIdentifierPrint).nif("Identifier"),
PrinterNIF(mlir_capi.Diagnostic, c.mlirDiagnosticPrint).nif("Diagnostic"),
} ++ Printer.entries;
17 changes: 10 additions & 7 deletions test/block_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,11 @@ defmodule BlockTest do

assert {:error,
[
{:error, _, "reference to block defined in another region", 1},
{:note, _, "see current operation: \"cf.br\"(%0)[INVALIDBLOCK] : (i32) -> ()", 0}
{:error, _, "reference to block defined in another region",
[
{:note, _, "see current operation: \"cf.br\"(%0)[INVALIDBLOCK] : (i32) -> ()",
[]}
]}
]} = res
end

Expand All @@ -80,8 +83,8 @@ defmodule BlockTest do
|> MLIR.verify()

assert [
{:error, _, "branch has 1 operands for successor #0, but target block has 0", 1},
{:note, _, "see current operation:" <> _, 0}
{:error, _, "branch has 1 operands for successor #0, but target block has 0",
[{:note, _, "see current operation:" <> _, []}]}
] = diagnostics
end

Expand Down Expand Up @@ -120,8 +123,8 @@ defmodule BlockTest do
|> MLIR.verify()

assert [
{:error, _, "branch has 1 operands for successor #0, but target block has 0", 1},
{:note, _, "see current operation" <> _, 0}
{:error, _, "branch has 1 operands for successor #0, but target block has 0",
[{:note, _, "see current operation" <> _, []}]}
] = diagnostics
end

Expand Down Expand Up @@ -159,7 +162,7 @@ defmodule BlockTest do
describe "insert block to region" do
for action <- [:insert, :append] do
test "appending #{action}", %{ctx: ctx} do
assert {:error, [{:error, _, "empty block: expect at least a terminator", 0}]} =
assert {:error, [{:error, _, "empty block: expect at least a terminator", []}]} =
BlockHelper.create_ir_by_action(ctx, unquote(action))
end
end
Expand Down
Loading

0 comments on commit 6d4a4e6

Please sign in to comment.