Skip to content

Commit

Permalink
Throw informative error when parsing reference sets in macros (#3653)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow committed Jan 16, 2024
1 parent b026f67 commit c31d9cf
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 5 deletions.
40 changes: 35 additions & 5 deletions src/Containers/macro.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,23 @@ function parse_macro_arguments(
end

"""
_explicit_oneto(index_set)
_explicit_oneto(error_fn, index_set)
If the `index_set` matches the form of `1:N`, then return
`Base.OneTo(index_set)`.
"""
function _explicit_oneto(index_set)
function _explicit_oneto(error_fn, index_set)
s = Meta.isexpr(index_set, :escape) ? index_set.args[1] : index_set
index_set = quote
try
$index_set
catch
$error_fn(
"unexpected error parsing reference set: ",
$(Meta.quot(_drop_esc(index_set))),
)
end
end
if Meta.isexpr(s, :call, 3) && s.args[1] == :(:) && s.args[2] == 1
return :(Base.OneTo($index_set))
else
Expand Down Expand Up @@ -316,6 +326,8 @@ function build_error_fn(macro_name, args, source)
return error_fn
end

_drop_esc(x) = Meta.isexpr(x, :escape) ? x.args[1] : x

"""
build_ref_sets(error_fn::Function, expr)
Expand All @@ -331,7 +343,7 @@ function build_ref_sets(error_fn::Function, expr)
end
if !_has_dependent_sets(index_vars, index_sets) && condition == :()
# Convert any 1:N to Base.OneTo(N)
new_index_sets = _explicit_oneto.(index_sets)
new_index_sets = _explicit_oneto.(error_fn, index_sets)
indices = :(Containers.vectorized_product($(new_index_sets...)))
return index_vars, indices
end
Expand All @@ -340,11 +352,29 @@ function build_ref_sets(error_fn::Function, expr)
for i in 1:length(index_vars)
push!(
indices.args,
:(($(esc_index_vars[1:(i-1)]...),) -> $(index_sets[i])),
quote
($(esc_index_vars[1:(i-1)]...),) -> try
$(index_sets[i])
catch
$error_fn(
"unexpected error parsing reference set: ",
$(Meta.quot(_drop_esc(index_sets[i]))),
)
end
end,
)
end
if condition != :()
f = :(($(esc_index_vars...),) -> $(esc(condition)))
f = quote
($(esc_index_vars...),) -> try
$(esc(condition))
catch
$error_fn(
"unexpected error parsing condition: ",
$(Meta.quot(condition)),
)
end
end
args = indices.args[2:end]
indices = :(Containers.nested($(args...); condition = $f))
end
Expand Down
78 changes: 78 additions & 0 deletions test/test_macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2254,4 +2254,82 @@ function test_escaping_of_set_kwarg()
return
end

function test_error_parsing_reference_sets()
model = Model()
@variable(model, a)
@test_throws_runtime(
ErrorException(
"In `@variable(model, b[1:a])`: unexpected error parsing reference set: 1:a",
),
@variable(model, b[1:a]),
)
@test_throws_runtime(
ErrorException(
"In `@variable(model, b[1:2, 1:a])`: unexpected error parsing reference set: 1:a",
),
@variable(model, b[1:2, 1:a]),
)
@test_throws_runtime(
ErrorException(
"In `@variable(model, b[i = 1:a, 1:i])`: unexpected error parsing reference set: 1:a",
),
@variable(model, b[i = 1:a, 1:i]),
)
@test_throws_runtime(
ErrorException(
"In `@variable(model, b[i = 1:2, a:i])`: unexpected error parsing reference set: a:i",
),
@variable(model, b[i = 1:2, a:i]),
)
@test_throws_runtime(
ErrorException(
"In `@variable(model, b[i = 1:2; i < a])`: unexpected error parsing condition: i < a",
),
@variable(model, b[i = 1:2; i < a]),
)
@test_throws_runtime(
ErrorException(
"In `@expression(model, b[1:a], a + 1)`: unexpected error parsing reference set: 1:a",
),
@expression(model, b[1:a], a + 1),
)
@test_throws_runtime(
ErrorException(
"In `@expression(model, b[1:2, 1:a], a + 1)`: unexpected error parsing reference set: 1:a",
),
@expression(model, b[1:2, 1:a], a + 1),
)
@test_throws_runtime(
ErrorException(
"In `@expression(model, b[i = 1:a, 1:i], a + 1)`: unexpected error parsing reference set: 1:a",
),
@expression(model, b[i = 1:a, 1:i], a + 1),
)
@test_throws_runtime(
ErrorException(
"In `@expression(model, b[i = 1:2, a:i], a + 1)`: unexpected error parsing reference set: a:i",
),
@expression(model, b[i = 1:2, a:i], a + 1),
)
@test_throws_runtime(
ErrorException(
"In `@expression(model, b[i = 1:2; i < a], a + 1)`: unexpected error parsing condition: i < a",
),
@expression(model, b[i = 1:2; i < a], a + 1),
)
@test_throws_runtime(
ErrorException(
"In `@constraint(model, b[i = 1:a], a <= 1)`: unexpected error parsing reference set: 1:a",
),
@constraint(model, b[i = 1:a], a <= 1),
)
@test_throws_runtime(
ErrorException(
"In `@constraint(model, b[i = 1:2; i < a], a <= 1)`: unexpected error parsing condition: i < a",
),
@constraint(model, b[i = 1:2; i < a], a <= 1),
)
return
end

end # module

0 comments on commit c31d9cf

Please sign in to comment.