Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throw informative error when parsing reference sets in macros #3653

Merged
merged 4 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
odow marked this conversation as resolved.
Show resolved Hide resolved

end # module
Loading