Skip to main content

dada_ir_sym/check/
report.rs

1use std::{panic::Location, sync::Arc};
2
3use dada_ir_ast::{
4    ast::SpannedBinaryOp,
5    diagnostic::{Diagnostic, Level, Reported},
6    span::Span,
7};
8use serde::Serialize;
9
10use crate::{
11    check::{debug::export, env::Env, predicates::Predicate},
12    ir::{
13        exprs::{SymExpr, SymPlaceExpr},
14        generics::SymWhereClause,
15        indices::InferVarIndex,
16        types::{SymGenericTerm, SymPerm, SymPlace, SymTy, SymTyName},
17        variables::SymVariable,
18    },
19};
20
21use super::{
22    inference::{Direction, InferVarKind},
23    red::{RedPerm, RedTy},
24    to_red::RedTyExt,
25};
26
27/// The `OrElse` trait captures error reporting context.
28/// Primitive type operations like subtyping are given an `&dyn OrElse<'db>`
29/// as argument. If the subtyping operation fails, it invokes the [`OrElse::report`][]
30/// method to report the error.
31///
32/// `OrElse` objects can be converted, using the [`OrElse::to_arc`][] method,
33/// into an [`ArcOrElse<'db>`][], which allows the or-else to be preserved
34/// for longer than the current stack frame. This is used to store an or-else in
35/// inference variable data so that, if a conflict is later generated, we can
36/// extract the reason for that original constraint.
37pub trait OrElse<'db> {
38    /// Report the diagnostic created by [`OrElse::or_else`][].
39    #[track_caller]
40    fn report(&self, env: &mut Env<'db>, because: Because<'db>) -> Reported {
41        let diagnostic = self.or_else(env, because);
42        env.report(diagnostic)
43    }
44
45    /// Create a diagnostic representing the error.
46    ///
47    /// The error would typically be expressed in high-level terms, like
48    /// "cannot assign from `a` to `b`" or "incorrect type of function argument".
49    ///
50    /// The `because` argument signals the reason the low-level operation failed
51    /// and will be used to provide additional details, like "`our` is not assignable to `my`".
52    fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic;
53
54    /// Convert a `&dyn OrElse<'db>` into an `ArcOrElse<'db>` so that it can be
55    /// stored in an [`InferenceVarData`](`crate::check::inference::InferenceVarData`)
56    /// or otherwise preserved beyond the current stack frame.
57    /// See the trait comment for more details.
58    fn to_arc(&self) -> ArcOrElse<'db>;
59
60    /// Returns the location in the *compiler source* where this `or_else` was created.
61    /// Useful for debugging.
62    fn compiler_location(&self) -> &'static Location<'static>;
63}
64
65/// See [`OrElse::to_arc`][].
66#[derive(Clone)]
67pub struct ArcOrElse<'db> {
68    data: Arc<dyn OrElse<'db> + 'db>,
69}
70
71impl<'db, T> From<Arc<T>> for ArcOrElse<'db>
72where
73    T: OrElse<'db> + 'db,
74{
75    fn from(data: Arc<T>) -> Self {
76        ArcOrElse { data }
77    }
78}
79
80impl<'db> OrElse<'db> for ArcOrElse<'db> {
81    fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic {
82        self.data.or_else(env, because)
83    }
84
85    fn to_arc(&self) -> ArcOrElse<'db> {
86        ArcOrElse::clone(self)
87    }
88
89    fn compiler_location(&self) -> &'static Location<'static> {
90        self.data.compiler_location()
91    }
92}
93
94impl<'db> std::ops::Deref for ArcOrElse<'db> {
95    type Target = dyn OrElse<'db> + 'db;
96
97    fn deref(&self) -> &Self::Target {
98        &*self.data
99    }
100}
101
102impl Serialize for ArcOrElse<'_> {
103    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
104    where
105        S: serde::Serializer,
106    {
107        #[derive(Serialize)]
108        struct ErasedOrElse {
109            compiler_location: export::CompilerLocation<'static>,
110        }
111
112        ErasedOrElse {
113            compiler_location: self.compiler_location().into(),
114        }
115        .serialize(serializer)
116    }
117}
118
119pub trait OrElseHelper<'db>: Sized {
120    /// Create a new [`OrElse`][] that just maps the [`Because`][]
121    /// value and propagates to an underlying or-else. This is used
122    /// when an operating like subtyping delegates to sub-operations:
123    /// the suboperation will provide a [`Because`][] value and
124    /// then the subtyping can provide additional context, but the
125    /// high-level 'or-else' is left unchanged.
126    fn map_because(
127        self,
128        f: impl 'db + Clone + Fn(Because<'db>) -> Because<'db>,
129    ) -> impl OrElse<'db>;
130}
131
132impl<'db> OrElseHelper<'db> for &dyn OrElse<'db> {
133    /// See [`OrElseHelper::map_because`][].
134    #[track_caller]
135    fn map_because(
136        self,
137        f: impl 'db + Clone + Fn(Because<'db>) -> Because<'db>,
138    ) -> impl OrElse<'db> {
139        struct MapBecause<F, G>(F, G, &'static Location<'static>);
140
141        impl<'db, F, G> OrElse<'db> for MapBecause<F, G>
142        where
143            F: 'db + Clone + Fn(Because<'db>) -> Because<'db>,
144            G: std::ops::Deref<Target: OrElse<'db>>,
145        {
146            fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic {
147                self.1.or_else(env, (self.0)(because))
148            }
149
150            fn to_arc(&self) -> ArcOrElse<'db> {
151                Arc::new(MapBecause(self.0.clone(), self.1.to_arc(), self.2)).into()
152            }
153
154            fn compiler_location(&self) -> &'static Location<'static> {
155                self.2
156            }
157        }
158
159        MapBecause(f, self, Location::caller())
160    }
161}
162
163/// Reason that a low-level typing operation failed.
164pub enum Because<'db> {
165    /// Miscellaneous
166    JustSo,
167
168    /// Universal variable was not declared to be `predicate` (and it must be)
169    VarNotDeclaredToBe(SymVariable<'db>, Predicate),
170
171    /// The never type is not copy
172    NeverIsNotCopy,
173
174    /// A where clause would be needed on the given variable
175    NoWhereClause(SymVariable<'db>, Predicate),
176
177    /// Struct types are never lent, even if they have lent things in them,
178    /// as they can still have non-lent things.
179    StructsAreNotLent(SymTyName<'db>),
180
181    /// $perm is not $predicate
182    PermIsNot(SymPerm<'db>, Predicate),
183
184    /// Leasing from a copy place yields a copy permission (which is not desired here)
185    LeasedFromCopyIsCopy(Vec<SymPlace<'db>>),
186
187    /// Universal mismatch
188    UniversalMismatch(SymVariable<'db>, SymVariable<'db>),
189
190    /// Name mismatch
191    NameMismatch(SymTyName<'db>, SymTyName<'db>),
192
193    /// Indicates that there was a previous constraint from elsewhere in the
194    /// program that caused a conflict with the current value
195    InferredPermBound(Direction, RedPerm<'db>, ArcOrElse<'db>),
196
197    /// Inference determined that the variable must have
198    /// this lower bound "or else" the given error would occur.
199    InferredLowerBound(RedTy<'db>, ArcOrElse<'db>),
200
201    /// The given inference variable needs more constraints
202    UnconstrainedInfer(InferVarIndex),
203}
204
205impl<'db> Because<'db> {
206    pub fn annotate_diagnostic(self, env: &mut Env<'db>, diagnostic: Diagnostic) -> Diagnostic {
207        let db = env.db();
208        let span = diagnostic.span.into_span(db);
209        if let Some(child) = self.to_annotation(env, span) {
210            diagnostic.child(child)
211        } else {
212            diagnostic
213        }
214    }
215
216    fn to_annotation(&self, env: &mut Env<'db>, span: Span<'db>) -> Option<Diagnostic> {
217        let db = env.db();
218        match self {
219            Because::JustSo => None,
220            Because::VarNotDeclaredToBe(v, predicate) => Some(Diagnostic::info(
221                db,
222                span,
223                format!(
224                    "to conclude that `{v}` is `{predicate}`, I would need you to add a declaration"
225                ),
226            )),
227            Because::NeverIsNotCopy => Some(Diagnostic::info(
228                db,
229                span,
230                "the never type (`!`) is not considered `copy`",
231            )),
232            Because::LeasedFromCopyIsCopy(places) => {
233                if places.len() == 1 {
234                    Some(Diagnostic::info(
235                        db,
236                        span,
237                        format!(
238                            "`{place}` is `copy`, so leasing from `{place}` yields a `copy` permission",
239                            place = places[0]
240                        ),
241                    ))
242                } else {
243                    Some(Diagnostic::info(
244                        db,
245                        span,
246                        format!(
247                            "{places} are all `copy`, so leasing from them yields a `copy` permission",
248                            places = anded_list(places),
249                        ),
250                    ))
251                }
252            }
253            Because::UniversalMismatch(v1, v2) => Some(Diagnostic::info(
254                db,
255                span,
256                format!("I cannot know whether `{v1}` and `{v2}` are the same"),
257            )),
258            Because::NameMismatch(n1, n2) => Some(Diagnostic::info(
259                db,
260                span,
261                format!("`{n1}` and `{n2}` are distinct types"),
262            )),
263            Because::InferredPermBound(direction, red_perm, or_else) => {
264                let or_else_diagnostic = or_else.or_else(env, Because::JustSo);
265                Some(
266                            Diagnostic::info(
267                                db,
268                                span,
269                                format!(
270                                    "I inferred that the perm must be {assignable_from_or_to} `{bound}` or else this error will occur",
271                                    assignable_from_or_to = match direction {
272                                        Direction::FromBelow => "assignable from",
273                                        Direction::FromAbove => "assignable to",
274                                    },
275                                    bound = format!("{red_perm:?}"), // FIXME
276                                ),
277                            )
278                            .child(or_else_diagnostic),
279                        )
280            }
281            Because::InferredLowerBound(red_ty, or_else) => {
282                let or_else_diagnostic = or_else.or_else(env, Because::JustSo);
283                Some(Diagnostic::info(
284                                db,
285                                span,
286                                format!(
287                                    "I inferred that the type `{red_ty}` is required because otherwise it would cause this error",
288                                    red_ty = red_ty.display(env),
289                                ),
290                            )
291                            .child(or_else_diagnostic))
292            }
293            Because::UnconstrainedInfer(infer) => Some(Diagnostic::info(
294                db,
295                env.infer_var_span(*infer),
296                "this error might well be bogus, I just can't infer the type here".to_string(),
297            )),
298            Because::NoWhereClause(var, predicate) => Some(Diagnostic::info(
299                db,
300                span,
301                format!("the variable `{var}` needs a where-clause to be considered `{predicate}`"),
302            )),
303            Because::StructsAreNotLent(s) => Some(Diagnostic::info(
304                db,
305                span,
306                format!("the struct type `{s}` is never considered `lent`"),
307            )),
308            Because::PermIsNot(perm, predicate) => Some(Diagnostic::info(
309                db,
310                span,
311                format!("the permission `{perm}` is not considered `{predicate}`"),
312            )),
313        }
314    }
315}
316
317fn anded_list<T>(v: &[T]) -> String
318where
319    T: std::fmt::Display,
320{
321    use std::fmt::Write;
322
323    let mut s = String::new();
324    let Some((last, prefix)) = v.split_last() else {
325        return s;
326    };
327
328    for p in prefix {
329        write!(s, "{p}, ").unwrap();
330    }
331
332    write!(s, "and {last}").unwrap();
333    s
334}
335
336/// Give a really bad subtype error.
337///
338/// Every usage of this is a bug.
339#[derive(Copy, Clone, Debug)]
340pub struct BadSubtermError<'db> {
341    span: Span<'db>,
342    lower: SymGenericTerm<'db>,
343    upper: SymGenericTerm<'db>,
344    compiler_location: &'static Location<'static>,
345}
346
347impl<'db> BadSubtermError<'db> {
348    #[track_caller]
349    pub fn new(
350        span: Span<'db>,
351        lower: impl Into<SymGenericTerm<'db>>,
352        upper: impl Into<SymGenericTerm<'db>>,
353    ) -> Self {
354        Self {
355            span,
356            lower: lower.into(),
357            upper: upper.into(),
358            compiler_location: Location::caller(),
359        }
360    }
361}
362
363impl<'db> OrElse<'db> for BadSubtermError<'db> {
364    fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic {
365        let db = env.db();
366        let Self {
367            span,
368            lower,
369            upper,
370            compiler_location: _,
371        } = *self;
372        because.annotate_diagnostic(
373            env,
374            Diagnostic::error(db, span, "subtype expected".to_string()).label(
375                db,
376                Level::Error,
377                span,
378                format!("expected `{upper}`, found `{lower}`"),
379            ),
380        )
381    }
382
383    fn to_arc(&self) -> ArcOrElse<'db> {
384        Arc::new(*self).into()
385    }
386
387    fn compiler_location(&self) -> &'static Location<'static> {
388        self.compiler_location
389    }
390}
391
392/// Give a really bad subtype error.
393///
394/// Every usage of this is a bug.
395#[derive(Copy, Clone, Debug)]
396pub struct WhereClauseError<'db> {
397    span: Span<'db>,
398    where_clause: SymWhereClause<'db>,
399    compiler_location: &'static Location<'static>,
400}
401
402impl<'db> WhereClauseError<'db> {
403    #[track_caller]
404    pub fn new(span: Span<'db>, where_clause: SymWhereClause<'db>) -> Self {
405        Self {
406            span,
407            where_clause,
408            compiler_location: Location::caller(),
409        }
410    }
411}
412
413impl<'db> OrElse<'db> for WhereClauseError<'db> {
414    fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic {
415        let db = env.db();
416        let Self {
417            span,
418            where_clause,
419            compiler_location: _,
420        } = *self;
421        because.annotate_diagnostic(
422            env,
423            Diagnostic::error(
424                db,
425                span,
426                "where clause on function not satisfied".to_string(),
427            )
428            .label(
429                db,
430                Level::Error,
431                span,
432                format!("expected `{where_clause:?}`"),
433            ),
434        )
435    }
436
437    fn to_arc(&self) -> ArcOrElse<'db> {
438        Arc::new(*self).into()
439    }
440
441    fn compiler_location(&self) -> &'static Location<'static> {
442        self.compiler_location
443    }
444}
445
446#[derive(Copy, Clone, Debug)]
447pub struct InvalidInitializerType<'db> {
448    variable: SymVariable<'db>,
449    variable_span: Span<'db>,
450    variable_ty: SymTy<'db>,
451    initializer: SymExpr<'db>,
452    compiler_location: &'static Location<'static>,
453}
454
455impl<'db> InvalidInitializerType<'db> {
456    #[track_caller]
457    pub fn new(
458        variable: SymVariable<'db>,
459        variable_span: Span<'db>,
460        variable_ty: SymTy<'db>,
461        initializer: SymExpr<'db>,
462    ) -> Self {
463        Self {
464            variable,
465            variable_span,
466            variable_ty,
467            initializer,
468            compiler_location: Location::caller(),
469        }
470    }
471}
472
473impl<'db> OrElse<'db> for InvalidInitializerType<'db> {
474    fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic {
475        let db = env.db();
476        let initializer_ty = self.initializer.ty(db);
477        because.annotate_diagnostic(
478            env,
479            Diagnostic::error(
480                db,
481                self.initializer.span(db),
482                format!(
483                    "variable `{v}` initialized with value of wrong type",
484                    v = self.variable
485                ),
486            )
487            .label(
488                db,
489                Level::Error,
490                self.initializer.span(db),
491                format!("initializer has type `{initializer_ty}`"),
492            )
493            .label(
494                db,
495                Level::Info,
496                self.variable_span,
497                format!(
498                    "`{v}` has type `{variable_ty}`",
499                    v = self.variable,
500                    variable_ty = self.variable_ty
501                ),
502            ),
503        )
504    }
505
506    fn to_arc(&self) -> ArcOrElse<'db> {
507        Arc::new(*self).into()
508    }
509
510    fn compiler_location(&self) -> &'static Location<'static> {
511        self.compiler_location
512    }
513}
514
515#[derive(Copy, Clone, Debug)]
516pub struct InvalidAssignmentType<'db> {
517    lhs: SymPlaceExpr<'db>,
518    rhs: SymExpr<'db>,
519    compiler_location: &'static Location<'static>,
520}
521
522impl<'db> InvalidAssignmentType<'db> {
523    #[track_caller]
524    pub fn new(lhs: SymPlaceExpr<'db>, rhs: SymExpr<'db>) -> Self {
525        Self {
526            lhs,
527            rhs,
528            compiler_location: Location::caller(),
529        }
530    }
531}
532
533impl<'db> OrElse<'db> for InvalidAssignmentType<'db> {
534    fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic {
535        let db = env.db();
536        let lhs_ty = self.lhs.ty(db);
537        let rhs_ty = self.rhs.ty(db);
538        because.annotate_diagnostic(
539            env,
540            Diagnostic::error(
541                db,
542                self.rhs.span(db),
543                "wrong type in assignment".to_string(),
544            )
545            .label(
546                db,
547                Level::Error,
548                self.rhs.span(db),
549                format!("this expression has type `{rhs_ty}`"),
550            )
551            .label(
552                db,
553                Level::Info,
554                self.lhs.span(db),
555                format!("I expected something assignable to this, which has type `{lhs_ty}`",),
556            ),
557        )
558    }
559
560    fn to_arc(&self) -> ArcOrElse<'db> {
561        Arc::new(*self).into()
562    }
563
564    fn compiler_location(&self) -> &'static Location<'static> {
565        self.compiler_location
566    }
567}
568
569#[derive(Copy, Clone, Debug)]
570pub struct InvalidReturnValue<'db> {
571    value: SymExpr<'db>,
572    return_ty: SymTy<'db>,
573    compiler_location: &'static Location<'static>,
574}
575
576impl<'db> InvalidReturnValue<'db> {
577    #[track_caller]
578    pub fn new(value: SymExpr<'db>, return_ty: SymTy<'db>) -> Self {
579        Self {
580            value,
581            return_ty,
582            compiler_location: Location::caller(),
583        }
584    }
585}
586
587impl<'db> OrElse<'db> for InvalidReturnValue<'db> {
588    fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic {
589        let db = env.db();
590        let value_ty = self.value.ty(db);
591        because.annotate_diagnostic(
592            env,
593            Diagnostic::error(db, self.value.span(db), "invalid return value".to_string())
594                .label(
595                    db,
596                    Level::Error,
597                    self.value.span(db),
598                    format!(
599                        "I expected a value of the return type, but this has type `{value_ty}`"
600                    ),
601                )
602                .label(
603                    db,
604                    Level::Info,
605                    // FIXME: with a bit of work, we could thread the span where return type is declared
606                    self.value.span(db),
607                    format!(
608                        "the return type is declared to be `{return_ty}`",
609                        return_ty = self.return_ty,
610                    ),
611                ),
612        )
613    }
614
615    fn to_arc(&self) -> ArcOrElse<'db> {
616        Arc::new(*self).into()
617    }
618
619    fn compiler_location(&self) -> &'static Location<'static> {
620        self.compiler_location
621    }
622}
623
624#[derive(Copy, Clone, Debug)]
625pub struct AwaitNonFuture<'db> {
626    await_span: Span<'db>,
627    future_expr: SymExpr<'db>,
628    compiler_location: &'static Location<'static>,
629}
630
631impl<'db> AwaitNonFuture<'db> {
632    #[track_caller]
633    pub fn new(await_span: Span<'db>, future_expr: SymExpr<'db>) -> Self {
634        Self {
635            await_span,
636            future_expr,
637            compiler_location: Location::caller(),
638        }
639    }
640}
641
642impl<'db> OrElse<'db> for AwaitNonFuture<'db> {
643    fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic {
644        let Self {
645            await_span,
646            future_expr,
647            compiler_location: _,
648        } = *self;
649        let db = env.db();
650        because.annotate_diagnostic(
651            env,
652            Diagnostic::error(
653                db,
654                await_span,
655                "`await` can only be used on futures".to_string(),
656            )
657            .label(
658                db,
659                Level::Error,
660                await_span,
661                "I expect `await` to be applied to a future",
662            )
663            .label(
664                db,
665                Level::Info,
666                future_expr.span(db),
667                format!(
668                    "this expression has type `{future_ty}`",
669                    future_ty = future_expr.ty(db),
670                ),
671            ),
672        )
673    }
674
675    fn to_arc(&self) -> ArcOrElse<'db> {
676        Arc::new(*self).into()
677    }
678
679    fn compiler_location(&self) -> &'static Location<'static> {
680        self.compiler_location
681    }
682}
683
684#[derive(Copy, Clone, Debug)]
685pub struct BooleanTypeRequired<'db> {
686    expr: SymExpr<'db>,
687    compiler_location: &'static Location<'static>,
688}
689
690impl<'db> BooleanTypeRequired<'db> {
691    #[track_caller]
692    pub fn new(expr: SymExpr<'db>) -> Self {
693        Self {
694            expr,
695            compiler_location: Location::caller(),
696        }
697    }
698}
699
700impl<'db> OrElse<'db> for BooleanTypeRequired<'db> {
701    fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic {
702        let db = env.db();
703        because.annotate_diagnostic(
704            env,
705            Diagnostic::error(
706                db,
707                self.expr.span(db),
708                "boolean expression required".to_string(),
709            )
710            .label(
711                db,
712                Level::Error,
713                self.expr.span(db),
714                format!(
715                    "I expected this expression to have a boolean type, but it has the type `{}`",
716                    self.expr.ty(db)
717                ),
718            ),
719        )
720    }
721
722    fn to_arc(&self) -> ArcOrElse<'db> {
723        Arc::new(*self).into()
724    }
725
726    fn compiler_location(&self) -> &'static Location<'static> {
727        self.compiler_location
728    }
729}
730
731#[derive(Copy, Clone, Debug)]
732pub struct NumericTypeExpected<'db> {
733    expr: SymExpr<'db>,
734    ty: SymTy<'db>,
735    compiler_location: &'static Location<'static>,
736}
737
738impl<'db> NumericTypeExpected<'db> {
739    #[track_caller]
740    pub fn new(expr: SymExpr<'db>, ty: SymTy<'db>) -> Self {
741        Self {
742            expr,
743            ty,
744            compiler_location: Location::caller(),
745        }
746    }
747}
748
749impl<'db> OrElse<'db> for NumericTypeExpected<'db> {
750    fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic {
751        let db = env.db();
752        because.annotate_diagnostic(
753            env,
754            Diagnostic::error(db, self.expr.span(db), "numeric type expected").label(
755                db,
756                Level::Error,
757                self.expr.span(db),
758                format!("I expected a numeric type but I found `{}`", self.ty),
759            ),
760        )
761    }
762
763    fn to_arc(&self) -> ArcOrElse<'db> {
764        Arc::new(*self).into()
765    }
766
767    fn compiler_location(&self) -> &'static Location<'static> {
768        self.compiler_location
769    }
770}
771
772#[derive(Copy, Clone, Debug)]
773pub struct OperatorRequiresNumericType<'db> {
774    op: SpannedBinaryOp<'db>,
775    expr: SymExpr<'db>,
776    compiler_location: &'static Location<'static>,
777}
778
779impl<'db> OperatorRequiresNumericType<'db> {
780    #[track_caller]
781    pub fn new(op: SpannedBinaryOp<'db>, expr: SymExpr<'db>) -> Self {
782        Self {
783            op,
784            expr,
785            compiler_location: Location::caller(),
786        }
787    }
788}
789
790impl<'db> OrElse<'db> for OperatorRequiresNumericType<'db> {
791    fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic {
792        let db = env.db();
793        let Self {
794            op: SpannedBinaryOp { span: op_span, op },
795            expr,
796            compiler_location: _,
797        } = *self;
798
799        because.annotate_diagnostic(
800            env,
801            Diagnostic::error(db, expr.span(db), "numeric type expected")
802                .label(
803                    db,
804                    Level::Error,
805                    expr.span(db),
806                    format!(
807                        "I expected this to have a numeric type but it had the type `{}`",
808                        expr.ty(db)
809                    ),
810                )
811                .label(
812                    db,
813                    Level::Info,
814                    op_span,
815                    format!("the operator `{op}` requires numeric arguments"),
816                ),
817        )
818    }
819
820    fn to_arc(&self) -> ArcOrElse<'db> {
821        Arc::new(*self).into()
822    }
823
824    fn compiler_location(&self) -> &'static Location<'static> {
825        self.compiler_location
826    }
827}
828
829#[derive(Copy, Clone, Debug)]
830pub struct OperatorArgumentsMustHaveSameType<'db> {
831    op: SpannedBinaryOp<'db>,
832    lhs: SymExpr<'db>,
833    rhs: SymExpr<'db>,
834    compiler_location: &'static Location<'static>,
835}
836
837impl<'db> OperatorArgumentsMustHaveSameType<'db> {
838    #[track_caller]
839    pub fn new(op: SpannedBinaryOp<'db>, lhs: SymExpr<'db>, rhs: SymExpr<'db>) -> Self {
840        Self {
841            op,
842            lhs,
843            rhs,
844            compiler_location: Location::caller(),
845        }
846    }
847}
848
849impl<'db> OrElse<'db> for OperatorArgumentsMustHaveSameType<'db> {
850    fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic {
851        let db = env.db();
852        let Self {
853            op: SpannedBinaryOp { span: op_span, op },
854            lhs,
855            rhs,
856            compiler_location: _,
857        } = *self;
858
859        because.annotate_diagnostic(
860            env,
861            Diagnostic::error(db, op_span, "same types expected")
862                .label(
863                    db,
864                    Level::Error,
865                    op_span,
866                    format!("I expected both arguments to `{op}` to have the same type",),
867                )
868                .label(
869                    db,
870                    Level::Info,
871                    lhs.span(db),
872                    format!("has type `{}`", lhs.ty(db)),
873                )
874                .label(
875                    db,
876                    Level::Info,
877                    rhs.span(db),
878                    format!("has type `{}`", rhs.ty(db)),
879                ),
880        )
881    }
882
883    fn to_arc(&self) -> ArcOrElse<'db> {
884        Arc::new(*self).into()
885    }
886
887    fn compiler_location(&self) -> &'static Location<'static> {
888        self.compiler_location
889    }
890}
891
892#[derive(Copy, Clone, Debug)]
893pub struct InferenceFallback<'db> {
894    span: Span<'db>,
895    kind: InferVarKind,
896    term: SymGenericTerm<'db>,
897    compiler_location: &'static Location<'static>,
898}
899
900impl<'db> InferenceFallback<'db> {
901    #[track_caller]
902    pub fn new(span: Span<'db>, kind: InferVarKind, term: impl Into<SymGenericTerm<'db>>) -> Self {
903        Self {
904            span,
905            kind,
906            term: term.into(),
907            compiler_location: Location::caller(),
908        }
909    }
910}
911
912impl<'db> OrElse<'db> for InferenceFallback<'db> {
913    fn or_else(&self, env: &mut Env<'db>, because: Because<'db>) -> Diagnostic {
914        let db = env.db();
915        let Self {
916            span,
917            kind,
918            term,
919            compiler_location: _,
920        } = *self;
921
922        because.annotate_diagnostic(
923            env,
924            Diagnostic::error(db, span, "unconstrained inference variable").label(
925                db,
926                Level::Error,
927                span,
928                format!("I resolved this {kind} to {term}",),
929            ),
930        )
931    }
932
933    fn to_arc(&self) -> ArcOrElse<'db> {
934        Arc::new(*self).into()
935    }
936
937    fn compiler_location(&self) -> &'static Location<'static> {
938        self.compiler_location
939    }
940}