1use super::liveness::LivenessResult;
11use super::types::*;
12use shape_ast::ast::Span;
13use std::collections::HashMap;
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
16pub struct ReturnReferenceSummary {
17 pub param_index: usize,
18 pub kind: BorrowKind,
19 pub projection: Option<Vec<ProjectionStep>>,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Hash)]
26pub struct ReferenceOrigin {
27 pub root: ReferenceOriginRoot,
28 pub projection: Vec<ProjectionStep>,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
32pub enum ReferenceOriginRoot {
33 Param(usize),
34 Local(SlotId),
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub enum LoanSinkKind {
39 ReturnSlot,
40 ClosureEnv,
41 ArrayStore,
42 ObjectStore,
43 EnumStore,
44 ArrayAssignment,
45 ObjectAssignment,
46 StructuredTaskBoundary,
47 DetachedTaskBoundary,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, Hash)]
51pub struct LoanSink {
52 pub loan_id: u32,
53 pub kind: LoanSinkKind,
54 pub sink_slot: Option<SlotId>,
56 pub span: Span,
57}
58
59#[derive(Debug)]
63pub struct BorrowAnalysis {
64 pub liveness: LivenessResult,
66 pub loans_at_point: HashMap<Point, Vec<LoanId>>,
68 pub loans: HashMap<LoanId, LoanInfo>,
70 pub errors: Vec<BorrowError>,
72 pub ownership_decisions: HashMap<Point, OwnershipDecision>,
74 pub mutability_errors: Vec<MutabilityError>,
76 pub return_reference_summary: Option<ReturnReferenceSummary>,
80}
81
82#[derive(Debug, Clone)]
84pub struct LoanInfo {
85 pub id: LoanId,
86 pub borrowed_place: Place,
88 pub kind: BorrowKind,
90 pub issued_at: Point,
92 pub span: Span,
94 pub region_depth: u32,
96}
97
98#[derive(Debug, Clone)]
101pub struct BorrowError {
102 pub kind: BorrowErrorKind,
103 pub span: Span,
105 pub conflicting_loan: LoanId,
107 pub loan_span: Span,
109 pub last_use_span: Option<Span>,
111 pub repairs: Vec<RepairCandidate>,
113}
114
115#[derive(Debug, Clone, PartialEq, Eq)]
116pub enum BorrowErrorKind {
117 ConflictSharedExclusive,
119 ConflictExclusiveExclusive,
121 ReadWhileExclusivelyBorrowed,
123 WriteWhileBorrowed,
125 ReferenceEscape,
127 ReferenceStoredInArray,
129 ReferenceStoredInObject,
131 ReferenceStoredInEnum,
133 ReferenceEscapeIntoClosure,
135 UseAfterMove,
137 ExclusiveRefAcrossTaskBoundary,
139 SharedRefAcrossDetachedTask,
141 InconsistentReferenceReturn,
144 CallSiteAliasConflict,
147 NonSendableAcrossTaskBoundary,
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
160pub enum BorrowErrorCode {
161 B0001,
163 B0002,
165 B0003,
167 B0004,
169 B0005,
171 B0006,
173 B0007,
175 B0012,
177 B0013,
179 B0014,
181}
182
183impl BorrowErrorCode {
184 pub fn as_str(self) -> &'static str {
186 match self {
187 BorrowErrorCode::B0001 => "B0001",
188 BorrowErrorCode::B0002 => "B0002",
189 BorrowErrorCode::B0003 => "B0003",
190 BorrowErrorCode::B0004 => "B0004",
191 BorrowErrorCode::B0005 => "B0005",
192 BorrowErrorCode::B0006 => "B0006",
193 BorrowErrorCode::B0007 => "B0007",
194 BorrowErrorCode::B0012 => "B0012",
195 BorrowErrorCode::B0013 => "B0013",
196 BorrowErrorCode::B0014 => "B0014",
197 }
198 }
199}
200
201impl std::fmt::Display for BorrowErrorCode {
202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 f.write_str(self.as_str())
204 }
205}
206
207impl BorrowErrorKind {
208 pub fn code(&self) -> BorrowErrorCode {
210 match self {
211 BorrowErrorKind::ConflictSharedExclusive
212 | BorrowErrorKind::ConflictExclusiveExclusive
213 | BorrowErrorKind::ReadWhileExclusivelyBorrowed => BorrowErrorCode::B0001,
214
215 BorrowErrorKind::WriteWhileBorrowed => BorrowErrorCode::B0002,
216
217 BorrowErrorKind::ReferenceEscape
218 | BorrowErrorKind::ReferenceEscapeIntoClosure => BorrowErrorCode::B0003,
219
220 BorrowErrorKind::ReferenceStoredInArray
221 | BorrowErrorKind::ReferenceStoredInObject
222 | BorrowErrorKind::ReferenceStoredInEnum => BorrowErrorCode::B0004,
223
224 BorrowErrorKind::UseAfterMove => BorrowErrorCode::B0005,
225
226 BorrowErrorKind::ExclusiveRefAcrossTaskBoundary => BorrowErrorCode::B0006,
227
228 BorrowErrorKind::SharedRefAcrossDetachedTask => BorrowErrorCode::B0012,
229
230 BorrowErrorKind::InconsistentReferenceReturn => BorrowErrorCode::B0007,
231
232 BorrowErrorKind::CallSiteAliasConflict => BorrowErrorCode::B0013,
233
234 BorrowErrorKind::NonSendableAcrossTaskBoundary => BorrowErrorCode::B0014,
235 }
236 }
237}
238
239#[derive(Debug, Clone)]
241pub struct RepairCandidate {
242 pub kind: RepairKind,
243 pub description: String,
245 pub diff: Option<RepairDiff>,
247}
248
249#[derive(Debug, Clone, PartialEq, Eq)]
250pub enum RepairKind {
251 Reorder,
253 Scope,
255 Clone,
257 Downgrade,
259 Extract,
261}
262
263#[derive(Debug, Clone)]
265pub struct RepairDiff {
266 pub removals: Vec<(Span, String)>,
268 pub additions: Vec<(Span, String)>,
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq)]
274pub enum OwnershipDecision {
275 Move,
277 Clone,
279 Copy,
281}
282
283#[derive(Debug, Clone)]
286pub struct FunctionBorrowSummary {
287 pub param_borrows: Vec<Option<BorrowKind>>,
289 pub conflict_pairs: Vec<(usize, usize)>,
291 pub return_summary: Option<ReturnReferenceSummary>,
294}
295
296#[derive(Debug, Clone)]
298pub struct MutabilityError {
299 pub span: Span,
301 pub variable_name: String,
303 pub declaration_span: Span,
305 pub is_explicit_let: bool,
307 pub is_const: bool,
309}
310
311impl BorrowAnalysis {
312 pub fn empty() -> Self {
314 BorrowAnalysis {
315 liveness: LivenessResult {
316 live_in: HashMap::new(),
317 live_out: HashMap::new(),
318 },
319 loans_at_point: HashMap::new(),
320 loans: HashMap::new(),
321 errors: Vec::new(),
322 ownership_decisions: HashMap::new(),
323 mutability_errors: Vec::new(),
324 return_reference_summary: None,
325 }
326 }
327
328 pub fn has_errors(&self) -> bool {
330 !self.errors.is_empty() || !self.mutability_errors.is_empty()
331 }
332
333 pub fn ownership_at(&self, point: Point) -> OwnershipDecision {
336 self.ownership_decisions
337 .get(&point)
338 .copied()
339 .unwrap_or(OwnershipDecision::Copy)
340 }
341
342 pub fn active_loans_at(&self, point: Point) -> &[LoanId] {
344 self.loans_at_point
345 .get(&point)
346 .map_or(&[], |v| v.as_slice())
347 }
348
349 pub fn loan(&self, id: LoanId) -> Option<&LoanInfo> {
351 self.loans.get(&id)
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use super::*;
358
359 #[test]
360 fn test_empty_analysis() {
361 let analysis = BorrowAnalysis::empty();
362 assert!(!analysis.has_errors());
363 assert_eq!(analysis.ownership_at(Point(0)), OwnershipDecision::Copy);
364 assert!(analysis.active_loans_at(Point(0)).is_empty());
365 }
366
367 #[test]
372 fn test_conflict_shared_exclusive_maps_to_b0001() {
373 assert_eq!(
374 BorrowErrorKind::ConflictSharedExclusive.code(),
375 BorrowErrorCode::B0001
376 );
377 }
378
379 #[test]
380 fn test_conflict_exclusive_exclusive_maps_to_b0001() {
381 assert_eq!(
382 BorrowErrorKind::ConflictExclusiveExclusive.code(),
383 BorrowErrorCode::B0001
384 );
385 }
386
387 #[test]
388 fn test_read_while_exclusively_borrowed_maps_to_b0001() {
389 assert_eq!(
390 BorrowErrorKind::ReadWhileExclusivelyBorrowed.code(),
391 BorrowErrorCode::B0001
392 );
393 }
394
395 #[test]
396 fn test_write_while_borrowed_maps_to_b0002() {
397 assert_eq!(
398 BorrowErrorKind::WriteWhileBorrowed.code(),
399 BorrowErrorCode::B0002
400 );
401 }
402
403 #[test]
404 fn test_reference_escape_maps_to_b0003() {
405 assert_eq!(
406 BorrowErrorKind::ReferenceEscape.code(),
407 BorrowErrorCode::B0003
408 );
409 }
410
411 #[test]
412 fn test_reference_escape_into_closure_maps_to_b0003() {
413 assert_eq!(
414 BorrowErrorKind::ReferenceEscapeIntoClosure.code(),
415 BorrowErrorCode::B0003
416 );
417 }
418
419 #[test]
420 fn test_reference_stored_in_array_maps_to_b0004() {
421 assert_eq!(
422 BorrowErrorKind::ReferenceStoredInArray.code(),
423 BorrowErrorCode::B0004
424 );
425 }
426
427 #[test]
428 fn test_reference_stored_in_object_maps_to_b0004() {
429 assert_eq!(
430 BorrowErrorKind::ReferenceStoredInObject.code(),
431 BorrowErrorCode::B0004
432 );
433 }
434
435 #[test]
436 fn test_reference_stored_in_enum_maps_to_b0004() {
437 assert_eq!(
438 BorrowErrorKind::ReferenceStoredInEnum.code(),
439 BorrowErrorCode::B0004
440 );
441 }
442
443 #[test]
444 fn test_use_after_move_maps_to_b0005() {
445 assert_eq!(
446 BorrowErrorKind::UseAfterMove.code(),
447 BorrowErrorCode::B0005
448 );
449 }
450
451 #[test]
452 fn test_exclusive_ref_across_task_boundary_maps_to_b0006() {
453 assert_eq!(
454 BorrowErrorKind::ExclusiveRefAcrossTaskBoundary.code(),
455 BorrowErrorCode::B0006
456 );
457 }
458
459 #[test]
460 fn test_inconsistent_reference_return_maps_to_b0007() {
461 assert_eq!(
462 BorrowErrorKind::InconsistentReferenceReturn.code(),
463 BorrowErrorCode::B0007
464 );
465 }
466
467 #[test]
468 fn test_borrow_error_code_as_str() {
469 assert_eq!(BorrowErrorCode::B0001.as_str(), "B0001");
470 assert_eq!(BorrowErrorCode::B0002.as_str(), "B0002");
471 assert_eq!(BorrowErrorCode::B0003.as_str(), "B0003");
472 assert_eq!(BorrowErrorCode::B0004.as_str(), "B0004");
473 assert_eq!(BorrowErrorCode::B0005.as_str(), "B0005");
474 assert_eq!(BorrowErrorCode::B0006.as_str(), "B0006");
475 assert_eq!(BorrowErrorCode::B0007.as_str(), "B0007");
476 }
477
478 #[test]
479 fn test_borrow_error_code_display() {
480 assert_eq!(format!("{}", BorrowErrorCode::B0001), "B0001");
481 assert_eq!(format!("{}", BorrowErrorCode::B0007), "B0007");
482 }
483
484 #[test]
485 fn test_all_error_kinds_have_codes() {
486 let all_kinds = vec![
488 BorrowErrorKind::ConflictSharedExclusive,
489 BorrowErrorKind::ConflictExclusiveExclusive,
490 BorrowErrorKind::ReadWhileExclusivelyBorrowed,
491 BorrowErrorKind::WriteWhileBorrowed,
492 BorrowErrorKind::ReferenceEscape,
493 BorrowErrorKind::ReferenceStoredInArray,
494 BorrowErrorKind::ReferenceStoredInObject,
495 BorrowErrorKind::ReferenceStoredInEnum,
496 BorrowErrorKind::ReferenceEscapeIntoClosure,
497 BorrowErrorKind::UseAfterMove,
498 BorrowErrorKind::ExclusiveRefAcrossTaskBoundary,
499 BorrowErrorKind::SharedRefAcrossDetachedTask,
500 BorrowErrorKind::InconsistentReferenceReturn,
501 ];
502 for kind in all_kinds {
503 let _code = kind.code();
505 }
506 }
507}