diff --git a/src/frontend/Semantic_error.ml b/src/frontend/Semantic_error.ml index a09b4e926f..5d38005adc 100644 --- a/src/frontend/Semantic_error.ml +++ b/src/frontend/Semantic_error.ml @@ -34,6 +34,8 @@ module TypeError = struct | ReturningFnExpectedNonReturningFound of string | ReturningFnExpectedNonFnFound of string | ReturningFnExpectedUndeclaredIdentFound of string + | ReturningFnExpectedUndeclaredDistSuffixFound of string * string + | ReturningFnExpectedWrongDistSuffixFound of string * string | NonReturningFnExpectedReturningFound of string | NonReturningFnExpectedNonFnFound of string | NonReturningFnExpectedUndeclaredIdentFound of string @@ -163,6 +165,22 @@ module TypeError = struct "A returning function was expected but an undeclared identifier \ '%s' was supplied." fn_name + | ReturningFnExpectedUndeclaredDistSuffixFound (prefix, suffix) -> + Fmt.pf ppf "Function '%s_%s' is not implemented for distribution '%s'." + prefix suffix prefix + | ReturningFnExpectedWrongDistSuffixFound (prefix, suffix) -> + let newsuffix = + match suffix with + | "lpdf" -> "lpmf" + | "lupdf" -> "lupmf" + | "lpmf" -> "lpdf" + | "lupmf" -> "lupdf" + | _ -> raise_s [%message "This should never happen."] + in + Fmt.pf ppf + "Function '%s_%s' is not implemented for distribution '%s', use \ + '%s_%s' instead." + prefix suffix prefix prefix newsuffix | NonReturningFnExpectedUndeclaredIdentFound fn_name -> Fmt.pf ppf "A non-returning function was expected but an undeclared identifier \ @@ -487,6 +505,16 @@ let returning_fn_expected_nonfn_found loc name = let returning_fn_expected_undeclaredident_found loc name = TypeError (loc, TypeError.ReturningFnExpectedUndeclaredIdentFound name) +let returning_fn_expected_undeclared_dist_suffix_found loc (prefix, suffix) = + TypeError + ( loc + , TypeError.ReturningFnExpectedUndeclaredDistSuffixFound (prefix, suffix) + ) + +let returning_fn_expected_wrong_dist_suffix_found loc (prefix, suffix) = + TypeError + (loc, TypeError.ReturningFnExpectedWrongDistSuffixFound (prefix, suffix)) + let nonreturning_fn_expected_returning_found loc name = TypeError (loc, TypeError.NonReturningFnExpectedReturningFound name) diff --git a/src/frontend/Semantic_error.mli b/src/frontend/Semantic_error.mli index b264c61ff5..3434ad932a 100644 --- a/src/frontend/Semantic_error.mli +++ b/src/frontend/Semantic_error.mli @@ -36,6 +36,12 @@ val returning_fn_expected_nonfn_found : Location_span.t -> string -> t val returning_fn_expected_undeclaredident_found : Location_span.t -> string -> t +val returning_fn_expected_undeclared_dist_suffix_found : + Location_span.t -> string * string -> t + +val returning_fn_expected_wrong_dist_suffix_found : + Location_span.t -> string * string -> t + val illtyped_reduce_sum : Location_span.t -> string diff --git a/src/frontend/Typechecker.ml b/src/frontend/Typechecker.ml index 352b6a28b8..bd07650080 100644 --- a/src/frontend/Typechecker.ml +++ b/src/frontend/Typechecker.ml @@ -394,7 +394,38 @@ let check_fn ~is_cond_dist loc tenv id es = (Utils.normalized_name id.name)) -> Semantic_error.returning_fn_expected_nonfn_found loc id.name |> error | [] -> - Semantic_error.returning_fn_expected_undeclaredident_found loc id.name + ( match Utils.split_distribution_suffix id.name with + | Some (prefix, suffix) -> ( + let known_families = + List.map + ~f:(fun (_, y, _, _) -> y) + Stan_math_signatures.distributions + in + let is_known_family s = + List.mem known_families s ~equal:String.equal + in + match suffix with + | ("lpmf" | "lumpf") when Env.mem tenv (prefix ^ "_lpdf") -> + Semantic_error.returning_fn_expected_wrong_dist_suffix_found loc + (prefix, suffix) + | ("lpdf" | "lumdf") when Env.mem tenv (prefix ^ "_lpmf") -> + Semantic_error.returning_fn_expected_wrong_dist_suffix_found loc + (prefix, suffix) + | _ -> + if + is_known_family prefix + && List.mem ~equal:String.equal + Utils.cumulative_distribution_suffices_w_rng suffix + then + Semantic_error + .returning_fn_expected_undeclared_dist_suffix_found loc + (prefix, suffix) + else + Semantic_error.returning_fn_expected_undeclaredident_found loc + id.name ) + | None -> + Semantic_error.returning_fn_expected_undeclaredident_found loc + id.name ) |> error | _ (* a function *) -> ( match SignatureMismatch.returntype tenv id.name (get_arg_types es) with diff --git a/src/middle/Utils.ml b/src/middle/Utils.ml index d920a16005..ebe2713bf0 100644 --- a/src/middle/Utils.ml +++ b/src/middle/Utils.ml @@ -14,6 +14,13 @@ let conditioning_suffices = ["_lpdf"; "_lupdf"; "_lupmf"; "_lpmf"; "_cdf"; "_lcdf"; "_lccdf"] let conditioning_suffices_w_log = conditioning_suffices @ ["_log"] + +let cumulative_distribution_suffices = + ["cdf"; "lcdf"; "lccdf"; "cdf_log"; "ccdf_log"] + +let cumulative_distribution_suffices_w_rng = + cumulative_distribution_suffices @ ["rng"] + let is_user_ident = Fn.non (String.is_suffix ~suffix:"__") let unnormalized_suffix = function diff --git a/test/integration/bad/missing_dist_suffix/completely_undefined1.stan b/test/integration/bad/missing_dist_suffix/completely_undefined1.stan new file mode 100644 index 0000000000..c63bd5f237 --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/completely_undefined1.stan @@ -0,0 +1,3 @@ +model { + target += foo_lpdf(1); +} \ No newline at end of file diff --git a/test/integration/bad/missing_dist_suffix/completely_undefined2.stan b/test/integration/bad/missing_dist_suffix/completely_undefined2.stan new file mode 100644 index 0000000000..f4d73d7216 --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/completely_undefined2.stan @@ -0,0 +1,6 @@ +data { + int foo_lpmf; +} +model { + target += foo_lpdf(1); +} \ No newline at end of file diff --git a/test/integration/bad/missing_dist_suffix/deprecated_suffix.stan b/test/integration/bad/missing_dist_suffix/deprecated_suffix.stan new file mode 100644 index 0000000000..388dc8fab1 --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/deprecated_suffix.stan @@ -0,0 +1,5 @@ +data { +} +model { + target += von_mises_ccdf_log(1, 0,1); +} \ No newline at end of file diff --git a/test/integration/bad/missing_dist_suffix/dune b/test/integration/bad/missing_dist_suffix/dune new file mode 100644 index 0000000000..856a7fccef --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/dune @@ -0,0 +1 @@ +(include ../dune) diff --git a/test/integration/bad/missing_dist_suffix/lpmf_lpdf_replacement1.stan b/test/integration/bad/missing_dist_suffix/lpmf_lpdf_replacement1.stan new file mode 100644 index 0000000000..5086e45cf8 --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/lpmf_lpdf_replacement1.stan @@ -0,0 +1,6 @@ +data { +} +model { + // known family, known suffix, not implemented + target += binomial_lpdf(1|0,1); +} diff --git a/test/integration/bad/missing_dist_suffix/lpmf_lpdf_replacement2.stan b/test/integration/bad/missing_dist_suffix/lpmf_lpdf_replacement2.stan new file mode 100644 index 0000000000..c491b21ae1 --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/lpmf_lpdf_replacement2.stan @@ -0,0 +1,6 @@ +data { +} +model { + // known family, known suffix, not implemented + target += normal_lpmf(1|0,1); +} diff --git a/test/integration/bad/missing_dist_suffix/no_rng_suffix.stan b/test/integration/bad/missing_dist_suffix/no_rng_suffix.stan new file mode 100644 index 0000000000..b1e75603ad --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/no_rng_suffix.stan @@ -0,0 +1,3 @@ +generated quantities { + real x = multi_gp_cholesky_rng([[0,0],[0,0]], [1,0]); +} \ No newline at end of file diff --git a/test/integration/bad/missing_dist_suffix/non_existing_distribution_suffix1.stan b/test/integration/bad/missing_dist_suffix/non_existing_distribution_suffix1.stan new file mode 100644 index 0000000000..764fc91e39 --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/non_existing_distribution_suffix1.stan @@ -0,0 +1,5 @@ +data { +} +model { + target += von_mises_cdf(1|0,1); +} diff --git a/test/integration/bad/missing_dist_suffix/non_existing_distribution_suffix2.stan b/test/integration/bad/missing_dist_suffix/non_existing_distribution_suffix2.stan new file mode 100644 index 0000000000..150005f7bd --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/non_existing_distribution_suffix2.stan @@ -0,0 +1,5 @@ +data { +} +model { + target += von_mises_lcdf(1|0,1); +} diff --git a/test/integration/bad/missing_dist_suffix/non_existing_distribution_suffix3.stan b/test/integration/bad/missing_dist_suffix/non_existing_distribution_suffix3.stan new file mode 100644 index 0000000000..53d67c0cb1 --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/non_existing_distribution_suffix3.stan @@ -0,0 +1,5 @@ +data { +} +model { + target += von_mises_lccdf(1|0,1); +} diff --git a/test/integration/bad/missing_dist_suffix/non_existing_distribution_suffix4.stan b/test/integration/bad/missing_dist_suffix/non_existing_distribution_suffix4.stan new file mode 100644 index 0000000000..335d64e925 --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/non_existing_distribution_suffix4.stan @@ -0,0 +1,5 @@ +data { +} +model { + target += von_mises_notasuffix(1,0,1); +} diff --git a/test/integration/bad/missing_dist_suffix/stanc.expected b/test/integration/bad/missing_dist_suffix/stanc.expected new file mode 100644 index 0000000000..dcab377404 --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/stanc.expected @@ -0,0 +1,141 @@ + $ ../../../../../install/default/bin/stanc completely_undefined1.stan +Semantic error in 'completely_undefined1.stan', line 2, column 12 to column 23: + ------------------------------------------------- + 1: model { + 2: target += foo_lpdf(1); + ^ + 3: } + ------------------------------------------------- + +A returning function was expected but an undeclared identifier 'foo_lpdf' was supplied. + $ ../../../../../install/default/bin/stanc completely_undefined2.stan +Semantic error in 'completely_undefined2.stan', line 5, column 12 to column 23: + ------------------------------------------------- + 3: } + 4: model { + 5: target += foo_lpdf(1); + ^ + 6: } + ------------------------------------------------- + +Function 'foo_lpdf' is not implemented for distribution 'foo', use 'foo_lpmf' instead. + $ ../../../../../install/default/bin/stanc deprecated_suffix.stan +Semantic error in 'deprecated_suffix.stan', line 4, column 14 to column 40: + ------------------------------------------------- + 2: } + 3: model { + 4: target += von_mises_ccdf_log(1, 0,1); + ^ + 5: } + ------------------------------------------------- + +Function 'von_mises_ccdf_log' is not implemented for distribution 'von_mises'. + $ ../../../../../install/default/bin/stanc lpmf_lpdf_replacement1.stan +Semantic error in 'lpmf_lpdf_replacement1.stan', line 5, column 14 to column 34: + ------------------------------------------------- + 3: model { + 4: // known family, known suffix, not implemented + 5: target += binomial_lpdf(1|0,1); + ^ + 6: } + ------------------------------------------------- + +Function 'binomial_lpdf' is not implemented for distribution 'binomial', use 'binomial_lpmf' instead. + $ ../../../../../install/default/bin/stanc lpmf_lpdf_replacement2.stan +Semantic error in 'lpmf_lpdf_replacement2.stan', line 5, column 14 to column 32: + ------------------------------------------------- + 3: model { + 4: // known family, known suffix, not implemented + 5: target += normal_lpmf(1|0,1); + ^ + 6: } + ------------------------------------------------- + +Function 'normal_lpmf' is not implemented for distribution 'normal', use 'normal_lpdf' instead. + $ ../../../../../install/default/bin/stanc no_rng_suffix.stan +Semantic error in 'no_rng_suffix.stan', line 2, column 12 to column 55: + ------------------------------------------------- + 1: generated quantities { + 2: real x = multi_gp_cholesky_rng([[0,0],[0,0]], [1,0]); + ^ + 3: } + ------------------------------------------------- + +Function 'multi_gp_cholesky_rng' is not implemented for distribution 'multi_gp_cholesky'. + $ ../../../../../install/default/bin/stanc non_existing_distribution_suffix1.stan +Semantic error in 'non_existing_distribution_suffix1.stan', line 4, column 14 to column 34: + ------------------------------------------------- + 2: } + 3: model { + 4: target += von_mises_cdf(1|0,1); + ^ + 5: } + ------------------------------------------------- + +Function 'von_mises_cdf' is not implemented for distribution 'von_mises'. + $ ../../../../../install/default/bin/stanc non_existing_distribution_suffix2.stan +Semantic error in 'non_existing_distribution_suffix2.stan', line 4, column 14 to column 35: + ------------------------------------------------- + 2: } + 3: model { + 4: target += von_mises_lcdf(1|0,1); + ^ + 5: } + ------------------------------------------------- + +Function 'von_mises_lcdf' is not implemented for distribution 'von_mises'. + $ ../../../../../install/default/bin/stanc non_existing_distribution_suffix3.stan +Semantic error in 'non_existing_distribution_suffix3.stan', line 4, column 14 to column 36: + ------------------------------------------------- + 2: } + 3: model { + 4: target += von_mises_lccdf(1|0,1); + ^ + 5: } + ------------------------------------------------- + +Function 'von_mises_lccdf' is not implemented for distribution 'von_mises'. + $ ../../../../../install/default/bin/stanc non_existing_distribution_suffix4.stan +Semantic error in 'non_existing_distribution_suffix4.stan', line 4, column 14 to column 41: + ------------------------------------------------- + 2: } + 3: model { + 4: target += von_mises_notasuffix(1,0,1); + ^ + 5: } + ------------------------------------------------- + +A returning function was expected but an undeclared identifier 'von_mises_notasuffix' was supplied. + $ ../../../../../install/default/bin/stanc user_defined.stan +Semantic error in 'user_defined.stan', line 7, column 13 to column 27: + ------------------------------------------------- + 5: } + 6: model { + 7: target += bar_lpmf(19.2); + ^ + 8: } + ------------------------------------------------- + +Function 'bar_lpmf' is not implemented for distribution 'bar', use 'bar_lpdf' instead. + $ ../../../../../install/default/bin/stanc user_defined_no_cdf.stan +Semantic error in 'user_defined_no_cdf.stan', line 7, column 14 to column 29: + ------------------------------------------------- + 5: } + 6: model { + 7: target += bar_lcdf(1|0,1); + ^ + 8: } + ------------------------------------------------- + +A returning function was expected but an undeclared identifier 'bar_lcdf' was supplied. + $ ../../../../../install/default/bin/stanc user_defined_no_rng.stan +Semantic error in 'user_defined_no_rng.stan', line 7, column 12 to column 23: + ------------------------------------------------- + 5: } + 6: generated quantities { + 7: real x = bar_rng(10); + ^ + 8: } + ------------------------------------------------- + +A returning function was expected but an undeclared identifier 'bar_rng' was supplied. diff --git a/test/integration/bad/missing_dist_suffix/user_defined.stan b/test/integration/bad/missing_dist_suffix/user_defined.stan new file mode 100644 index 0000000000..26a4d5dcd6 --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/user_defined.stan @@ -0,0 +1,8 @@ +functions { + real bar_lpdf(real x){ + return 1.0; + } +} +model { + target += bar_lpmf(19.2); +} \ No newline at end of file diff --git a/test/integration/bad/missing_dist_suffix/user_defined_no_cdf.stan b/test/integration/bad/missing_dist_suffix/user_defined_no_cdf.stan new file mode 100644 index 0000000000..cadd1e4977 --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/user_defined_no_cdf.stan @@ -0,0 +1,8 @@ +functions { + real bar_lpdf(real x){ + return 1.0; + } +} +model { + target += bar_lcdf(1|0,1); +} \ No newline at end of file diff --git a/test/integration/bad/missing_dist_suffix/user_defined_no_rng.stan b/test/integration/bad/missing_dist_suffix/user_defined_no_rng.stan new file mode 100644 index 0000000000..b00cd3b6b1 --- /dev/null +++ b/test/integration/bad/missing_dist_suffix/user_defined_no_rng.stan @@ -0,0 +1,8 @@ +functions { + real bar_lpdf(real x){ + return 1.0; + } +} +generated quantities { + real x = bar_rng(10); +} \ No newline at end of file diff --git a/test/integration/tfp/stan_models/test_mat_corr.stan b/test/integration/tfp/stan_models/test_mat_corr.stan new file mode 100644 index 0000000000..0298b3fedc --- /dev/null +++ b/test/integration/tfp/stan_models/test_mat_corr.stan @@ -0,0 +1,9 @@ +data { + int K; +} +parameters { + cholesky_factor_corr[K] L; +} +model { + target += lkj_corr_cholesky_lpdf(L|2.5); +} \ No newline at end of file