Skip to content
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
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* text=auto eol=lf
*.rda binary
26 changes: 26 additions & 0 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,32 @@ compile_functions <- function(env, verbose = FALSE, global = FALSE) {
prep_fun_cpp(funs[ind], fun_end, env$hpp_code)
})

reserved_names <- unique(
unlist(
lapply(stan_funs, function(stan_fun) {
regmatches(
stan_fun,
gregexpr("(?<=_stan_)[[:alnum:]_]+", stan_fun, perl = TRUE)
)[[1]]
}),
use.names = FALSE
)
)

if (length(reserved_names) > 0) {
stop(
paste0(
"expose_functions() can't expose this Stan function because the function ",
"name and/or one or more argument names use a reserved keyword ",
"(typically in the C++ toolchain used to compile Stan). Please rename ",
"the function/arguments in your Stan functions block and try again. ",
"Conflicting names: ",
paste(reserved_names, collapse = ", ")
),
call. = FALSE
)
}

env$fun_names <- sapply(seq_len(length(funs) - 1), function(ind) {
get_function_name(funs[ind], funs[ind + 1], env$hpp_code)
})
Expand Down
80 changes: 80 additions & 0 deletions tests/testthat/test-model-expose-functions.R
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,86 @@ test_that("Overloaded functions give meaningful errors", {
"Overloaded functions are currently not able to be exposed to R! The following overloaded functions were found: fun1, fun3")
})

reserved_names_msg <- function(names) {
paste(
"expose_functions() can't expose this Stan function because the function",
"name and/or one or more argument names use a reserved keyword",
"(typically in the C++ toolchain used to compile Stan). Please rename",
"the function/arguments in your Stan functions block and try again.",
paste("Conflicting names:", paste(names, collapse = ", "))
)
}

test_that("Reserved names in Stan code give the same error", {
stan_file <- write_stan_file(
"
functions {
real min(real max, real class) {
return max - class;
}
}
"
)

funmod <- cmdstan_model(stan_file, force_recompile = TRUE)
expect_error(
funmod$expose_functions(),
reserved_names_msg(c("min", "max", "class")),
fixed = TRUE
)
})

test_that("Multiple reserved C++ keywords in Stan code give the same error", {
stan_file <- write_stan_file(
"
functions {
real template(real class, real namespace, real private) {
return class + namespace + private;
}
}
"
)

funmod <- cmdstan_model(stan_file, force_recompile = TRUE)
expect_error(
funmod$expose_functions(),
reserved_names_msg(c("template", "class", "namespace", "private")),
fixed = TRUE
)
})

test_that("Reserved keywords in Stan function bodies are allowed", {
stan_file <- write_stan_file(
"
functions {
real add_one(real x) {
real class = 1;
return x + class;
}
}
"
)

funmod <- cmdstan_model(stan_file, force_recompile = TRUE)
expect_no_error(funmod$expose_functions())
expect_equal(funmod$functions$add_one(2), 3)
})

test_that("Stan code with no reserved names exposes functions", {
stan_file <- write_stan_file(
"
functions {
real add_pair(real left, real right) {
return left + right;
}
}
"
)

funmod <- cmdstan_model(stan_file, force_recompile = TRUE)
expect_no_error(funmod$expose_functions())
})

test_that("Exposing external functions errors before v2.32", {
fake_cmdstan_version("2.26.0")

Expand Down
Loading