1#![feature(
4 associated_type_defaults,
5 box_patterns,
6 if_let_guard,
7 min_specialization,
8 never_type,
9 rustc_private,
10 unwrap_infallible
11)]
12
13extern crate rustc_abi;
14extern crate rustc_data_structures;
15extern crate rustc_errors;
16extern crate rustc_hir;
17extern crate rustc_index;
18extern crate rustc_infer;
19extern crate rustc_middle;
20extern crate rustc_mir_dataflow;
21extern crate rustc_span;
22extern crate rustc_type_ir;
23
24mod checker;
25pub mod compare_impl_item;
26mod ghost_statements;
27pub mod invariants;
28mod primops;
29mod queue;
30mod type_env;
31
32use checker::{Checker, trait_impl_subtyping};
33use flux_common::{dbg, result::ResultExt as _};
34use flux_infer::{
35 fixpoint_encoding::FixQueryCache,
36 infer::{ConstrReason, SubtypeReason, Tag},
37};
38use flux_macros::fluent_messages;
39use flux_middle::{
40 FixpointQueryKind,
41 def_id::MaybeExternId,
42 global_env::GlobalEnv,
43 rty::{self, ESpan},
44 timings,
45};
46use rustc_data_structures::unord::UnordMap;
47use rustc_errors::ErrorGuaranteed;
48use rustc_hir::def_id::LocalDefId;
49use rustc_span::Span;
50
51use crate::{checker::errors::ResultExt as _, ghost_statements::compute_ghost_statements};
52
53fluent_messages! { "../locales/en-US.ftl" }
54
55fn report_fixpoint_errors(
56 genv: GlobalEnv,
57 local_id: LocalDefId,
58 errors: Vec<Tag>,
59) -> Result<(), ErrorGuaranteed> {
60 #[expect(clippy::collapsible_else_if, reason = "it looks better")]
61 if genv.should_fail(local_id) {
62 if errors.is_empty() { report_expected_neg(genv, local_id) } else { Ok(()) }
63 } else {
64 if errors.is_empty() { Ok(()) } else { report_errors(genv, errors) }
65 }
66}
67
68pub fn check_fn(
69 genv: GlobalEnv,
70 cache: &mut FixQueryCache,
71 def_id: LocalDefId,
72) -> Result<(), ErrorGuaranteed> {
73 let span = genv.tcx().def_span(def_id);
74
75 if !genv.tcx().def_span(def_id).ctxt().is_root() {
80 return Ok(());
81 }
82
83 if genv.tcx().hir_node_by_def_id(def_id).body_id().is_none() {
85 return Ok(());
86 }
87
88 let opts = genv.infer_opts(def_id);
89
90 if let Some(infcx_root) = trait_impl_subtyping(genv, def_id, opts, span)
92 .with_span(span)
93 .map_err(|err| err.emit(genv, def_id))?
94 {
95 tracing::info!("check_fn::refine-subtyping");
96 let errors = infcx_root
97 .execute_fixpoint_query(cache, MaybeExternId::Local(def_id), FixpointQueryKind::Impl)
98 .emit(&genv)?;
99 tracing::info!("check_fn::fixpoint-subtyping");
100 report_fixpoint_errors(genv, def_id, errors)?;
101 }
102
103 if genv.trusted(def_id) {
105 return Ok(());
106 }
107
108 timings::time_it(timings::TimingKind::CheckFn(def_id), || -> Result<(), ErrorGuaranteed> {
109 let ghost_stmts = compute_ghost_statements(genv, def_id)
110 .with_span(span)
111 .map_err(|err| err.emit(genv, def_id))?;
112 let mut closures = UnordMap::default();
113 let shape_result =
115 Checker::run_in_shape_mode(genv, def_id, &ghost_stmts, &mut closures, opts)
116 .map_err(|err| err.emit(genv, def_id))?;
117
118 let infcx_root = Checker::run_in_refine_mode(
120 genv,
121 def_id,
122 &ghost_stmts,
123 &mut closures,
124 shape_result,
125 opts,
126 )
127 .map_err(|err| err.emit(genv, def_id))?;
128
129 let errors = infcx_root
131 .execute_fixpoint_query(cache, MaybeExternId::Local(def_id), FixpointQueryKind::Body)
132 .emit(&genv)?;
133 report_fixpoint_errors(genv, def_id, errors)
134 })?;
135
136 dbg::check_fn_span!(genv.tcx(), def_id).in_scope(|| Ok(()))
137}
138
139fn call_error(genv: GlobalEnv, span: Span, dst_span: Option<ESpan>) -> ErrorGuaranteed {
140 genv.sess()
141 .emit_err(errors::RefineError::call(span, dst_span))
142}
143
144fn ret_error(genv: GlobalEnv, span: Span, dst_span: Option<ESpan>) -> ErrorGuaranteed {
145 genv.sess()
146 .emit_err(errors::RefineError::ret(span, dst_span))
147}
148
149fn report_errors(genv: GlobalEnv, errors: Vec<Tag>) -> Result<(), ErrorGuaranteed> {
150 let mut e = None;
151 for err in errors {
152 let span = err.src_span;
153 e = Some(match err.reason {
154 ConstrReason::Call
155 | ConstrReason::Subtype(SubtypeReason::Input)
156 | ConstrReason::Subtype(SubtypeReason::Requires)
157 | ConstrReason::Predicate => call_error(genv, span, err.dst_span),
158 ConstrReason::Assign => genv.sess().emit_err(errors::AssignError { span }),
159 ConstrReason::Ret
160 | ConstrReason::Subtype(SubtypeReason::Output)
161 | ConstrReason::Subtype(SubtypeReason::Ensures) => ret_error(genv, span, err.dst_span),
162 ConstrReason::Div => genv.sess().emit_err(errors::DivError { span }),
163 ConstrReason::Rem => genv.sess().emit_err(errors::RemError { span }),
164 ConstrReason::Goto(_) => genv.sess().emit_err(errors::GotoError { span }),
165 ConstrReason::Assert(msg) => genv.sess().emit_err(errors::AssertError { span, msg }),
166 ConstrReason::Fold | ConstrReason::FoldLocal => {
167 genv.sess().emit_err(errors::FoldError { span })
168 }
169 ConstrReason::Overflow => genv.sess().emit_err(errors::OverflowError { span }),
170 ConstrReason::Underflow => genv.sess().emit_err(errors::UnderflowError { span }),
171 ConstrReason::Other => genv.sess().emit_err(errors::UnknownError { span }),
172 });
173 }
174
175 if let Some(e) = e { Err(e) } else { Ok(()) }
176}
177
178fn report_expected_neg(genv: GlobalEnv, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
179 Err(genv.sess().emit_err(errors::ExpectedNeg {
180 span: genv.tcx().def_span(def_id),
181 def_descr: genv.tcx().def_descr(def_id.to_def_id()),
182 }))
183}
184
185mod errors {
186 use flux_errors::E0999;
187 use flux_macros::{Diagnostic, Subdiagnostic};
188 use flux_middle::rty::ESpan;
189 use rustc_span::Span;
190
191 #[derive(Diagnostic)]
192 #[diag(refineck_goto_error, code = E0999)]
193 pub struct GotoError {
194 #[primary_span]
195 pub span: Span,
196 }
197
198 #[derive(Diagnostic)]
199 #[diag(refineck_assign_error, code = E0999)]
200 pub struct AssignError {
201 #[primary_span]
202 pub span: Span,
203 }
204
205 #[derive(Subdiagnostic)]
206 #[note(refineck_condition_span_note)]
207 pub(crate) struct ConditionSpanNote {
208 #[primary_span]
209 pub span: Span,
210 }
211
212 #[derive(Subdiagnostic)]
213 #[note(refineck_call_span_note)]
214 pub(crate) struct CallSpanNote {
215 #[primary_span]
216 pub span: Span,
217 }
218
219 #[derive(Diagnostic)]
220 #[diag(refineck_refine_error, code = E0999)]
221 pub struct RefineError {
222 #[primary_span]
223 #[label]
224 pub span: Span,
225 cond: &'static str,
226 #[subdiagnostic]
227 span_note: Option<ConditionSpanNote>,
228 #[subdiagnostic]
229 call_span_note: Option<CallSpanNote>,
230 }
231
232 impl RefineError {
233 pub fn call(span: Span, espan: Option<ESpan>) -> Self {
234 RefineError::new("precondition", span, espan)
235 }
236
237 pub fn ret(span: Span, espan: Option<ESpan>) -> Self {
238 RefineError::new("postcondition", span, espan)
239 }
240
241 fn new(cond: &'static str, span: Span, espan: Option<ESpan>) -> RefineError {
242 match espan {
243 Some(dst_span) => {
244 let span_note = Some(ConditionSpanNote { span: dst_span.span });
245 let call_span_note = dst_span.base.map(|span| CallSpanNote { span });
246 RefineError { span, cond, span_note, call_span_note }
247 }
248 None => RefineError { span, cond, span_note: None, call_span_note: None },
249 }
250 }
251 }
252
253 #[derive(Diagnostic)]
254 #[diag(refineck_div_error, code = E0999)]
255 pub struct DivError {
256 #[primary_span]
257 pub span: Span,
258 }
259
260 #[derive(Diagnostic)]
261 #[diag(refineck_rem_error, code = E0999)]
262 pub struct RemError {
263 #[primary_span]
264 pub span: Span,
265 }
266
267 #[derive(Diagnostic)]
268 #[diag(refineck_assert_error, code = E0999)]
269 pub struct AssertError {
270 #[primary_span]
271 pub span: Span,
272 pub msg: &'static str,
273 }
274
275 #[derive(Diagnostic)]
276 #[diag(refineck_fold_error, code = E0999)]
277 pub struct FoldError {
278 #[primary_span]
279 pub span: Span,
280 }
281
282 #[derive(Diagnostic)]
283 #[diag(refineck_overflow_error, code = E0999)]
284 pub struct OverflowError {
285 #[primary_span]
286 pub span: Span,
287 }
288
289 #[derive(Diagnostic)]
290 #[diag(refineck_underflow_error, code = E0999)]
291 pub struct UnderflowError {
292 #[primary_span]
293 pub span: Span,
294 }
295
296 #[derive(Diagnostic)]
297 #[diag(refineck_unknown_error, code = E0999)]
298 pub struct UnknownError {
299 #[primary_span]
300 pub span: Span,
301 }
302
303 #[derive(Diagnostic)]
304 #[diag(refineck_expected_neg, code = E0999)]
305 pub struct ExpectedNeg {
306 #[primary_span]
307 pub span: Span,
308 pub def_descr: &'static str,
309 }
310}