Skip to main content

dada_ir_sym/check/
exprs.rs

1use std::panic::Location;
2
3use crate::{
4    check::{
5        env::Env,
6        member_lookup::MemberLookup,
7        scope::{NameResolution, NameResolutionSym},
8        scope_tree::ScopeTreeNode,
9    },
10    ir::{
11        binder::Binder,
12        classes::SymAggregate,
13        exprs::{
14            SymBinaryOp, SymByteLiteral, SymByteLiteralData, SymExpr, SymExprKind, SymLiteral,
15            SymMatchArm, SymPlaceExpr, SymPlaceExprKind,
16        },
17        functions::{SymFunction, SymInputOutput},
18        types::{SymGenericKind, SymGenericTerm, SymTy, SymTyKind, SymTyName},
19        variables::{FromVar, SymVariable},
20    },
21    prelude::CheckedSignature,
22    well_known,
23};
24use dada_ir_ast::{
25    ast::{
26        AstBinaryOp, AstExpr, AstExprKind, AstGenericTerm, Identifier, LiteralKind, PermissionOp,
27        SpanVec, SpannedBinaryOp, SpannedIdentifier, UnaryOp,
28    },
29    diagnostic::{Diagnostic, Err, Level, Reported},
30    span::{Span, Spanned},
31};
32use dada_parser::prelude::*;
33use dada_util::{FromImpls, boxed_async_fn};
34use serde::Serialize;
35
36use super::{
37    CheckExprInEnv, CheckTyInEnv,
38    debug::TaskDescription,
39    live_places::LivePlaces,
40    report::{
41        AwaitNonFuture, BadSubtermError, InvalidAssignmentType, InvalidReturnValue,
42        NumericTypeExpected, OperatorArgumentsMustHaveSameType, OperatorRequiresNumericType,
43        WhereClauseError,
44    },
45    temporaries::Temporary,
46};
47
48#[derive(Clone, Serialize)]
49pub(crate) struct ExprResult<'db> {
50    /// List of [`Temporary`][] variables created by this expression.
51    pub temporaries: Vec<Temporary<'db>>,
52
53    /// Span of the expression
54    pub span: Span<'db>,
55
56    /// The primary result from translating an expression.
57    /// Note that an ast-expr can result in many kinds of things.
58    pub kind: ExprResultKind<'db>,
59}
60
61#[derive(Clone, Debug, FromImpls, Serialize)]
62pub(crate) enum ExprResultKind<'db> {
63    /// An expression identifying a place in memory (e.g., a local variable).
64    PlaceExpr(SymPlaceExpr<'db>),
65
66    /// An expression that produces a value.
67    Expr(SymExpr<'db>),
68
69    /// A partially completed method call.
70    #[no_from_impl]
71    Method {
72        self_expr: SymExpr<'db>,
73        id_span: Span<'db>,
74        function: SymFunction<'db>,
75        generics: Option<SpanVec<'db, AstGenericTerm<'db>>>,
76    },
77
78    /// Some kind of name resoluton that cannot be represented by as an expression.
79    Other(NameResolution<'db>),
80}
81
82impl<'db> CheckExprInEnv<'db> for AstExpr<'db> {
83    type Output = ExprResult<'db>;
84
85    async fn check_in_env(&self, env: &mut Env<'db>, live_after: LivePlaces) -> Self::Output {
86        check_expr(self, env, live_after).await
87    }
88}
89
90#[boxed_async_fn]
91async fn check_expr<'db>(
92    expr: &AstExpr<'db>,
93    env: &mut Env<'db>,
94    live_after: LivePlaces,
95) -> ExprResult<'db> {
96    env.indent("check_expr", &[expr], async |env| {
97        let db = env.db();
98        let expr_span = expr.span;
99
100        match &*expr.kind {
101            AstExprKind::Literal(literal) => match literal.kind(db) {
102                LiteralKind::Integer => {
103                    let ty = env.fresh_ty_inference_var(expr_span);
104                    let bits = match str::parse(literal.text(db)) {
105                        Ok(v) => v,
106                        Err(e) => panic!("error: {e:?}"),
107                    };
108                    let sym_expr = SymExpr::new(
109                        db,
110                        expr_span,
111                        ty,
112                        SymExprKind::Primitive(SymLiteral::Integral { bits }),
113                    );
114                    env.spawn_require_my_numeric_type(
115                        LivePlaces::fixme(),
116                        ty,
117                        &NumericTypeExpected::new(sym_expr, ty),
118                    );
119                    ExprResult {
120                        temporaries: vec![],
121                        span: expr_span,
122                        kind: sym_expr.into(),
123                    }
124                }
125
126                LiteralKind::String => {
127                    // Generate `String.literal(b"...", length)`
128
129                    // Generate `b"..."`
130                    let bytes = literal.text(db).as_bytes();
131                    let byte_literal_expr = {
132                        let pointer_struct = match well_known::pointer_struct(db) {
133                            Ok(v) => v,
134                            Err(reported) => return ExprResult::err(db, reported),
135                        };
136                        let data = SymByteLiteralData::new(db, bytes);
137                        let byte_literal = SymByteLiteral::new(db, expr_span, data);
138                        SymExpr::new(
139                            db,
140                            expr_span,
141                            SymTy::named(db, pointer_struct.into(), vec![SymTy::u8(db).into()]),
142                            SymExprKind::ByteLiteral(byte_literal),
143                        )
144                    };
145
146                    // Generate `length`
147                    let len_literal_expr = {
148                        let value = bytes.len() as u64;
149                        SymExpr::new(
150                            db,
151                            expr_span,
152                            SymTy::u32(db),
153                            SymExprKind::Primitive(SymLiteral::Integral { bits: value }),
154                        )
155                    };
156
157                    // Generate and return `String.literal(b"...", length)`
158                    let mut temporaries = vec![];
159                    let ctor_call_expr = {
160                        let literal_fn = match well_known::string_literal_fn(db) {
161                            Ok(v) => v,
162                            Err(reported) => return ExprResult::err(db, reported),
163                        };
164                        SymExpr::new(
165                            db,
166                            expr_span,
167                            SymTy::string(db),
168                            SymExprKind::Call {
169                                function: literal_fn,
170                                substitution: vec![],
171                                arg_temps: vec![
172                                    byte_literal_expr.into_temporary_var(db, &mut temporaries),
173                                    len_literal_expr.into_temporary_var(db, &mut temporaries),
174                                ],
175                            },
176                        )
177                    };
178                    ExprResult {
179                        temporaries,
180                        span: expr_span,
181                        kind: ctor_call_expr.into(),
182                    }
183                }
184
185                LiteralKind::Boolean => {
186                    let bits = match &literal.text(db)[..] {
187                        "true" => 1,
188                        "false" => 0,
189                        t => panic!("unrecognized boolean literal {t:?}"),
190                    };
191                    ExprResult {
192                        temporaries: vec![],
193                        span: expr_span,
194                        kind: SymExpr::new(
195                            db,
196                            expr_span,
197                            SymTy::boolean(db),
198                            SymExprKind::Primitive(SymLiteral::Integral { bits }),
199                        )
200                        .into(),
201                    }
202                }
203            },
204
205            AstExprKind::Tuple(span_vec) => {
206                let mut temporaries = vec![];
207                let mut exprs = vec![];
208                for element in &span_vec.values {
209                    exprs.push(
210                        element
211                            .check_in_env(env, LivePlaces::fixme())
212                            .await
213                            .into_expr(env, &mut temporaries),
214                    );
215                }
216
217                let ty = SymTy::new(
218                    db,
219                    SymTyKind::Named(
220                        SymTyName::Tuple { arity: exprs.len() },
221                        exprs.iter().map(|e| e.ty(db).into()).collect(),
222                    ),
223                );
224
225                ExprResult {
226                    temporaries,
227                    span: expr_span,
228                    kind: ExprResultKind::Expr(SymExpr::new(
229                        db,
230                        expr_span,
231                        ty,
232                        SymExprKind::Tuple(exprs),
233                    )),
234                }
235            }
236
237            AstExprKind::BinaryOp(span_op, lhs, rhs) => {
238                let span_op: SpannedBinaryOp<'db> = *span_op;
239                match span_op.op {
240                    AstBinaryOp::Add | AstBinaryOp::Sub | AstBinaryOp::Mul | AstBinaryOp::Div => {
241                        let mut temporaries: Vec<Temporary<'db>> = vec![];
242                        let lhs: SymExpr<'db> = lhs
243                            .check_in_env(env, LivePlaces::fixme())
244                            .await
245                            .into_expr(env, &mut temporaries);
246                        let rhs: SymExpr<'db> = rhs
247                            .check_in_env(env, LivePlaces::fixme())
248                            .await
249                            .into_expr(env, &mut temporaries);
250
251                        // For now, let's do a dumb rule that operands must be
252                        // of the same primitive (and scalar) type.
253
254                        env.spawn_require_numeric_type(
255                            lhs.ty(db),
256                            &OperatorRequiresNumericType::new(span_op, lhs),
257                        );
258                        env.spawn_require_numeric_type(
259                            rhs.ty(db),
260                            &OperatorRequiresNumericType::new(span_op, rhs),
261                        );
262                        env.spawn_if_not_never(&[lhs.ty(db), rhs.ty(db)], async move |env| {
263                            env.spawn_require_equal_types(
264                                live_after,
265                                lhs.ty(db),
266                                rhs.ty(db),
267                                &OperatorArgumentsMustHaveSameType::new(span_op, lhs, rhs),
268                            );
269                        });
270
271                        // What type do we want these operators to have?
272                        // For now I'll just take the LHS, but that seems
273                        // wrong if e.g. one side is `!`, then we probably
274                        // want `!`, right?
275
276                        ExprResult::from_expr(
277                            env.db(),
278                            SymExpr::new(
279                                db,
280                                expr_span,
281                                lhs.ty(db),
282                                SymExprKind::BinaryOp(
283                                    SymBinaryOp::try_from(span_op.op).expect("invalid binary op"),
284                                    lhs,
285                                    rhs,
286                                ),
287                            ),
288                            temporaries,
289                        )
290                    }
291
292                    AstBinaryOp::AndAnd => {
293                        let mut temporaries: Vec<Temporary<'db>> = vec![];
294                        let lhs: SymExpr<'db> = lhs
295                            .check_in_env(env, LivePlaces::fixme())
296                            .await
297                            .into_expr(env, &mut temporaries);
298                        let rhs: SymExpr<'db> = rhs
299                            .check_in_env(env, live_after)
300                            .await
301                            .into_expr(env, &mut temporaries);
302                        env.require_expr_has_bool_ty(LivePlaces::fixme(), lhs);
303                        env.require_expr_has_bool_ty(live_after, rhs);
304
305                        // construct an expression like
306                        // if lhs { if rhs { true } else { false } } else { false }
307                        ExprResult::from_expr(
308                            env.db(),
309                            SymExpr::if_then_else(
310                                db,
311                                expr_span,
312                                lhs,
313                                SymExpr::if_then_else(
314                                    db,
315                                    expr_span,
316                                    rhs,
317                                    SymExpr::true_literal(db, expr_span),
318                                    SymExpr::false_literal(db, expr_span),
319                                ),
320                                SymExpr::false_literal(db, expr_span),
321                            ),
322                            temporaries,
323                        )
324                    }
325
326                    AstBinaryOp::OrOr => {
327                        let mut temporaries: Vec<Temporary<'db>> = vec![];
328                        let lhs: SymExpr<'db> = lhs
329                            .check_in_env(env, LivePlaces::fixme())
330                            .await
331                            .into_expr(env, &mut temporaries);
332                        let rhs: SymExpr<'db> = rhs
333                            .check_in_env(env, live_after)
334                            .await
335                            .into_expr(env, &mut temporaries);
336
337                        env.require_expr_has_bool_ty(LivePlaces::fixme(), lhs);
338                        env.require_expr_has_bool_ty(live_after, rhs);
339
340                        // construct an expression like
341                        // if lhs { true } else { if rhs { true } else { false } }
342                        ExprResult::from_expr(
343                            env.db(),
344                            SymExpr::if_then_else(
345                                db,
346                                expr_span,
347                                lhs,
348                                SymExpr::true_literal(db, expr_span),
349                                SymExpr::if_then_else(
350                                    db,
351                                    expr_span,
352                                    rhs,
353                                    SymExpr::true_literal(db, expr_span),
354                                    SymExpr::false_literal(db, expr_span),
355                                ),
356                            ),
357                            temporaries,
358                        )
359                    }
360
361                    AstBinaryOp::GreaterThan
362                    | AstBinaryOp::LessThan
363                    | AstBinaryOp::GreaterEqual
364                    | AstBinaryOp::LessEqual
365                    | AstBinaryOp::EqualEqual => {
366                        let mut temporaries: Vec<Temporary<'db>> = vec![];
367                        let lhs: SymExpr<'db> = lhs
368                            .check_in_env(env, LivePlaces::fixme())
369                            .await
370                            .into_expr(env, &mut temporaries);
371                        let rhs: SymExpr<'db> = rhs
372                            .check_in_env(env, LivePlaces::fixme())
373                            .await
374                            .into_expr(env, &mut temporaries);
375
376                        // For now, let's do a dumb rule that operands must be
377                        // of the same primitive (and scalar) type.
378
379                        env.spawn_require_numeric_type(
380                            lhs.ty(db),
381                            &OperatorRequiresNumericType::new(span_op, lhs),
382                        );
383                        env.spawn_require_numeric_type(
384                            rhs.ty(db),
385                            &OperatorRequiresNumericType::new(span_op, rhs),
386                        );
387                        env.spawn_if_not_never(&[lhs.ty(db), rhs.ty(db)], async move |env| {
388                            env.spawn_require_equal_types(
389                                LivePlaces::fixme(),
390                                lhs.ty(db),
391                                rhs.ty(db),
392                                &OperatorArgumentsMustHaveSameType::new(span_op, lhs, rhs),
393                            );
394                        });
395
396                        // What type do we want these operators to have?
397                        // For now I'll just take the LHS, but that seems
398                        // wrong if e.g. one side is `!`, then we probably
399                        // want `!`, right?
400
401                        ExprResult::from_expr(
402                            env.db(),
403                            SymExpr::new(
404                                db,
405                                expr_span,
406                                SymTy::boolean(db),
407                                SymExprKind::BinaryOp(
408                                    SymBinaryOp::try_from(span_op.op).expect("invalid binary op"),
409                                    lhs,
410                                    rhs,
411                                ),
412                            ),
413                            temporaries,
414                        )
415                    }
416
417                    AstBinaryOp::Assign => {
418                        let mut temporaries: Vec<Temporary<'db>> = vec![];
419                        let place: SymPlaceExpr<'db> = lhs
420                            .check_in_env(env, LivePlaces::fixme())
421                            .await
422                            .into_place_expr(env, &mut temporaries);
423                        let value: SymExpr<'db> = rhs
424                            .check_in_env(env, LivePlaces::fixme())
425                            .await
426                            .into_expr(env, &mut temporaries);
427
428                        // For now, let's do a dumb rule that operands must be
429                        // of the same primitive (and scalar) type.
430
431                        env.spawn_require_assignable_type(
432                            LivePlaces::fixme(),
433                            value.ty(db),
434                            place.ty(db),
435                            &InvalidAssignmentType::new(place, value),
436                        );
437
438                        ExprResult::from_expr(
439                            env.db(),
440                            SymExpr::new(
441                                db,
442                                expr_span,
443                                SymTy::unit(db),
444                                SymExprKind::Assign { place, value },
445                            ),
446                            temporaries,
447                        )
448                    }
449                }
450            }
451
452            AstExprKind::Id(SpannedIdentifier { span: id_span, id }) => {
453                match env.scope.resolve_name(db, *id, *id_span) {
454                    Err(reported) => ExprResult::err(db, reported),
455                    Ok(res) => ExprResult::from_name_resolution(env, res, expr_span).await,
456                }
457            }
458
459            AstExprKind::DotId(owner, id) => {
460                let mut owner_result = owner.check_in_env(env, live_after).await;
461                match owner_result.kind {
462                    ExprResultKind::PlaceExpr(_) | ExprResultKind::Expr(_) => {
463                        MemberLookup::new(env)
464                            .lookup_member(owner_result, *id)
465                            .await
466                    }
467
468                    ExprResultKind::Other(name_resolution) => {
469                        match name_resolution.resolve_relative_id(db, *id) {
470                            // Got an error? Bail out.
471                            Err(reported) => ExprResult::err(db, reported),
472
473                            // Found something with lexical resolution? Continue.
474                            Ok(Ok(r)) => ExprResult::from_name_resolution(env, r, expr_span).await,
475
476                            // Otherwise, try type-dependent lookup.
477                            Ok(Err(name_resolution)) => {
478                                owner_result.kind = name_resolution.into();
479                                MemberLookup::new(env)
480                                    .lookup_member(owner_result, *id)
481                                    .await
482                            }
483                        }
484                    }
485
486                    ExprResultKind::Method {
487                        self_expr: owner,
488                        function: method,
489                        ..
490                    } => ExprResult::err(
491                        db,
492                        report_missing_call_to_method(db, owner.span(db), method),
493                    ),
494                }
495            }
496
497            AstExprKind::SquareBracketOp(owner, square_bracket_args) => {
498                let owner_result = owner.check_in_env(env, LivePlaces::fixme()).await;
499                match owner_result.kind {
500                    ExprResultKind::Method {
501                        self_expr: owner,
502                        function: method,
503                        generics: None,
504                        id_span,
505                    } => {
506                        let ast_terms = square_bracket_args.parse_as_generics(db);
507
508                        ExprResult {
509                            kind: ExprResultKind::Method {
510                                self_expr: owner,
511                                function: method,
512                                generics: Some(ast_terms),
513                                id_span,
514                            },
515                            ..owner_result
516                        }
517                    }
518
519                    ExprResultKind::PlaceExpr(_) | ExprResultKind::Expr(_) => ExprResult::err(
520                        db,
521                        report_not_implemented(db, expr_span, "indexing expressions"),
522                    ),
523
524                    // We see something like `foo.bar[][]` where `bar` is a method.
525                    // The only correct thing here would be `foo.bar[]()[]`, i.e., call the method and then index.
526                    // We give an error under that assumption.
527                    // It seems likely we can do a better job.
528                    ExprResultKind::Method {
529                        self_expr: owner,
530                        function: method,
531                        generics: Some(_),
532                        ..
533                    } => ExprResult::err(
534                        db,
535                        report_missing_call_to_method(db, owner.span(db), method),
536                    ),
537
538                    ExprResultKind::Other(name_resolution) => {
539                        let generics = square_bracket_args.parse_as_generics(db);
540                        match name_resolution
541                            .resolve_relative_generic_args(env, &generics)
542                            .await
543                        {
544                            Ok(name_resolution) => ExprResult {
545                                temporaries: owner_result.temporaries,
546                                span: expr_span,
547                                kind: name_resolution.into(),
548                            },
549                            Err(r) => ExprResult::err(db, r),
550                        }
551                    }
552                }
553            }
554
555            AstExprKind::ParenthesisOp(owner, ast_args) => {
556                let owner_result = owner.check_in_env(env, live_after).await;
557                match owner_result {
558                    ExprResult {
559                        temporaries,
560                        span: expr_span,
561                        kind:
562                            ExprResultKind::Method {
563                                self_expr,
564                                id_span,
565                                function,
566                                generics,
567                            },
568                    } => {
569                        check_method_call(
570                            env,
571                            live_after,
572                            id_span,
573                            expr_span,
574                            function,
575                            Some(self_expr),
576                            ast_args,
577                            generics,
578                            temporaries,
579                        )
580                        .await
581                    }
582
583                    ExprResult {
584                        temporaries,
585                        span: function_span,
586                        kind:
587                            ExprResultKind::Other(NameResolution {
588                                generics,
589                                sym: NameResolutionSym::SymFunction(sym),
590                                ..
591                            }),
592                    } => {
593                        check_function_call(
594                            env,
595                            live_after,
596                            function_span,
597                            expr_span,
598                            sym,
599                            ast_args,
600                            generics,
601                            temporaries,
602                        )
603                        .await
604                    }
605
606                    // Calling a class like `Class(a, b)`: convert to
607                    // `Class.new(a, b)`.
608                    ExprResult {
609                        temporaries,
610                        span: class_span,
611                        kind:
612                            ExprResultKind::Other(
613                                name_resolution @ NameResolution {
614                                    sym: NameResolutionSym::SymAggregate(class_sym),
615                                    ..
616                                },
617                            ),
618                    } => {
619                        check_class_call(
620                            env,
621                            live_after,
622                            class_span,
623                            expr_span,
624                            name_resolution,
625                            class_sym,
626                            ast_args,
627                            temporaries,
628                        )
629                        .await
630                    }
631
632                    ExprResult {
633                        span: owner_span, ..
634                    } => {
635                        // FIXME: we probably want to support functions and function typed values?
636                        ExprResult::err(db, report_not_callable(db, owner_span))
637                    }
638                }
639            }
640
641            AstExprKind::Constructor(_ast_path, _span_vec) => todo!(),
642            AstExprKind::Return(ast_expr) => {
643                let mut temporaries = vec![];
644
645                let return_expr = if let Some(ast_expr) = ast_expr {
646                    ast_expr
647                        .check_in_env(env, LivePlaces::none(env))
648                        .await
649                        .into_expr(env, &mut temporaries)
650                } else {
651                    // the default is `return ()`
652                    SymExpr::new(db, expr_span, SymTy::unit(db), SymExprKind::Tuple(vec![]))
653                };
654
655                let Some(expected_return_ty) = env.return_ty else {
656                    return ExprResult::err(
657                        db,
658                        env.report(
659                            Diagnostic::error(
660                                db,
661                                expr_span,
662                                "unexpected `return` statement".to_string(),
663                            )
664                            .label(
665                                db,
666                                Level::Error,
667                                expr_span,
668                                "I did not expect to see a `return` statement here".to_string(),
669                            ),
670                        ),
671                    );
672                };
673
674                env.spawn_require_assignable_type(
675                    LivePlaces::none(env),
676                    return_expr.ty(db),
677                    expected_return_ty,
678                    &InvalidReturnValue::new(return_expr, expected_return_ty),
679                );
680
681                ExprResult {
682                    temporaries,
683                    span: expr_span,
684                    kind: SymExpr::new(
685                        db,
686                        expr_span,
687                        SymTy::never(db),
688                        SymExprKind::Return(return_expr),
689                    )
690                    .into(),
691                }
692            }
693
694            AstExprKind::Await {
695                future,
696                await_keyword,
697            } => {
698                let await_span = *await_keyword;
699
700                let mut temporaries = vec![];
701
702                let future_expr = future
703                    .check_in_env(env, live_after)
704                    .await
705                    .into_expr(env, &mut temporaries);
706                let future_ty = future_expr.ty(db);
707
708                let awaited_ty = env.fresh_ty_inference_var(await_span);
709
710                env.spawn_require_future_type(
711                    live_after,
712                    future_ty,
713                    awaited_ty,
714                    &AwaitNonFuture::new(await_span, future_expr),
715                );
716
717                ExprResult {
718                    temporaries,
719                    span: expr_span,
720                    kind: SymExpr::new(
721                        db,
722                        expr_span,
723                        awaited_ty,
724                        SymExprKind::Await {
725                            future: future_expr,
726                            await_keyword: await_span,
727                        },
728                    )
729                    .into(),
730                }
731            }
732            AstExprKind::UnaryOp(spanned_unary_op, ast_expr) => match spanned_unary_op.op {
733                UnaryOp::Not => {
734                    let mut temporaries = vec![];
735                    let operand = ast_expr
736                        .check_in_env(env, live_after)
737                        .await
738                        .into_expr(env, &mut temporaries);
739                    env.require_expr_has_bool_ty(live_after, operand);
740
741                    ExprResult {
742                        temporaries,
743                        span: expr_span,
744                        kind: SymExpr::new(
745                            db,
746                            expr_span,
747                            SymTy::boolean(db),
748                            SymExprKind::Not {
749                                operand,
750                                op_span: spanned_unary_op.span,
751                            },
752                        )
753                        .into(),
754                    }
755                }
756                UnaryOp::Negate => todo!(),
757            },
758
759            AstExprKind::Block(ast_block) => ExprResult {
760                temporaries: vec![],
761                span: expr_span,
762                kind: ast_block.check_in_env(env, live_after).await.into(),
763            },
764
765            AstExprKind::If(ast_arms) => {
766                let mut arms = vec![];
767                let mut has_else = false;
768                for arm in ast_arms {
769                    let condition = if let Some(c) = &arm.condition {
770                        let expr = c
771                            .check_in_env(env, LivePlaces::fixme())
772                            .await
773                            .into_expr_with_enclosed_temporaries(env);
774                        env.require_expr_has_bool_ty(LivePlaces::fixme(), expr);
775                        Some(expr)
776                    } else {
777                        has_else = true;
778                        None
779                    };
780
781                    let body = arm.result.check_in_env(env, live_after).await;
782
783                    arms.push(SymMatchArm { condition, body });
784                }
785
786                let if_ty = if !has_else {
787                    SymTy::unit(db)
788                } else {
789                    env.fresh_ty_inference_var(expr_span)
790                };
791
792                for arm in &arms {
793                    env.spawn_require_assignable_type(
794                        live_after,
795                        arm.body.ty(db),
796                        if_ty,
797                        &BadSubtermError::new(arm.body.span(db), arm.body.ty(db), if_ty),
798                    );
799                }
800
801                ExprResult {
802                    temporaries: vec![],
803                    span: expr_span,
804                    kind: SymExpr::new(db, expr_span, if_ty, SymExprKind::Match { arms }).into(),
805                }
806            }
807
808            AstExprKind::PermissionOp { value, op } => {
809                let mut temporaries = vec![];
810                let value_result = value.check_in_env(env, live_after).await;
811                let place_expr = value_result.into_place_expr(env, &mut temporaries);
812                let sym_place = place_expr.into_sym_place(db);
813                ExprResult {
814                    temporaries,
815                    span: expr_span,
816                    kind: SymExpr::new(
817                        db,
818                        expr_span,
819                        match op {
820                            PermissionOp::Mutate => place_expr.ty(db).mutable(db, sym_place),
821                            PermissionOp::Reference => place_expr.ty(db).referenced(db, sym_place),
822                            PermissionOp::Give => place_expr.ty(db),
823                            PermissionOp::Share => place_expr.ty(db).shared(db),
824                        },
825                        SymExprKind::PermissionOp(*op, place_expr),
826                    )
827                    .into(),
828                }
829            }
830        }
831    })
832    .await
833}
834
835#[boxed_async_fn]
836async fn check_class_call<'db>(
837    env: &mut Env<'db>,
838    live_after: LivePlaces,
839    class_span: Span<'db>,
840    expr_span: Span<'db>,
841    name_resolution: NameResolution<'db>,
842    class_sym: SymAggregate<'db>,
843    ast_args: &SpanVec<'db, AstExpr<'db>>,
844    temporaries: Vec<Temporary<'db>>,
845) -> ExprResult<'db> {
846    let db = env.db();
847
848    let new_ident = SpannedIdentifier {
849        span: class_span,
850        id: Identifier::new_ident(db),
851    };
852
853    let (generics, new_function) = match name_resolution.resolve_relative_id(db, new_ident) {
854        Ok(Ok(NameResolution {
855            generics,
856            sym: NameResolutionSym::SymFunction(m),
857        })) => (generics, m),
858        Ok(r) => return ExprResult::err(db, report_no_new_method(db, class_span, class_sym, r)),
859        Err(reported) => return ExprResult::err(db, reported),
860    };
861
862    check_function_call(
863        env,
864        live_after,
865        class_span,
866        expr_span,
867        new_function,
868        ast_args,
869        generics,
870        temporaries,
871    )
872    .await
873}
874
875fn report_no_new_method<'db>(
876    db: &'db dyn crate::Db,
877    class_span: Span<'db>,
878    class_sym: SymAggregate<'db>,
879    resolution: Result<NameResolution<'db>, NameResolution<'db>>,
880) -> Reported {
881    let mut diag = Diagnostic::error(
882        db,
883        class_span,
884        format!("the class `{class_sym}` has no `new` method"),
885    )
886    .label(
887        db,
888        Level::Error,
889        class_span,
890        format!("I could not find a `new` method on the class `{class_sym}`"),
891    );
892
893    match resolution {
894        Ok(name_resolution) => {
895            diag = diag.child(
896                Diagnostic::new(
897                    db,
898                    Level::Note,
899                    class_sym.name_span(db),
900                    "calling a class is equivalent to calling `new`, but `new` is not a method"
901                        .to_string(),
902                )
903                .label(
904                    db,
905                    Level::Note,
906                    name_resolution.span(db).unwrap(),
907                    format!(
908                        "I found a class member named `new` but it is {}, not a method",
909                        name_resolution.categorize(db)
910                    ),
911                ),
912            );
913        }
914        Err(_) => {
915            diag = diag.child(
916                Diagnostic::new(
917                    db,
918                    Level::Note,
919                    class_sym.name_span(db),
920                    format!(
921                        "calling a class is equivalent to calling `new`, but `{class_sym}` does not define a `new` method"
922                    ),
923                )
924                .label(
925                    db,
926                    Level::Note,
927                    class_sym.name_span(db),
928                    "I could not find any class member named `new`".to_string(),
929                ),
930            );
931        }
932    }
933
934    diag.report(db)
935}
936
937#[boxed_async_fn]
938async fn check_function_call<'db>(
939    env: &mut Env<'db>,
940    live_after: LivePlaces,
941    function_span: Span<'db>,
942    expr_span: Span<'db>,
943    function: SymFunction<'db>,
944    ast_args: &SpanVec<'db, AstExpr<'db>>,
945    generics: Vec<SymGenericTerm<'db>>,
946    temporaries: Vec<Temporary<'db>>,
947) -> ExprResult<'db> {
948    let db = env.db();
949
950    env.log("check_function_call", &[&function]);
951    env.log("generics", &[&generics]);
952
953    // Get the signature.
954    let signature = match function.checked_signature(db) {
955        Ok(signature) => signature,
956        Err(reported) => {
957            for ast_arg in ast_args {
958                let _ = ast_arg.check_in_env(env, LivePlaces::fixme()).await;
959            }
960            return ExprResult::err(db, reported);
961        }
962    };
963    let input_output = signature.input_output(db);
964
965    env.log("input_output", &[&input_output]);
966
967    // Create inference variables for any generic arguments not provided.
968    let expected_generics = function.transitive_generic_parameters(db);
969    env.log("expected_generics", &[&expected_generics]);
970    let mut substitution = generics.clone();
971    substitution.extend(
972        expected_generics[generics.len()..]
973            .iter()
974            .map(|&var| env.fresh_inference_var_term(var.kind(db), function_span)),
975    );
976
977    check_call_common(
978        env,
979        live_after,
980        function,
981        expr_span,
982        function_span,
983        input_output,
984        substitution,
985        ast_args,
986        None,
987        temporaries,
988    )
989    .await
990}
991
992/// Check a call like `a.b()` where `b` is a method.
993/// These are somewhat different than calls like `b(a)` because of how
994/// type arguments are handled.
995#[allow(clippy::too_many_arguments)]
996#[boxed_async_fn]
997async fn check_method_call<'db>(
998    env: &mut Env<'db>,
999    live_after: LivePlaces,
1000    id_span: Span<'db>,
1001    expr_span: Span<'db>,
1002    function: SymFunction<'db>,
1003    self_expr: Option<SymExpr<'db>>,
1004    ast_args: &[AstExpr<'db>],
1005    generics: Option<SpanVec<'db, AstGenericTerm<'db>>>,
1006    temporaries: Vec<Temporary<'db>>,
1007) -> ExprResult<'db> {
1008    let db = env.db();
1009
1010    // Get the signature.
1011    let signature = match function.checked_signature(db) {
1012        Ok(signature) => signature,
1013        Err(reported) => {
1014            for &generic in generics.iter().flatten() {
1015                let _ = generic.check_in_env(env).await;
1016            }
1017            for ast_arg in ast_args {
1018                let _ = ast_arg.check_in_env(env, LivePlaces::fixme()).await;
1019            }
1020            return ExprResult::err(db, reported);
1021        }
1022    };
1023    let input_output = signature.input_output(db);
1024
1025    // Prepare the substitution for the function.
1026    let substitution = match generics {
1027        None => {
1028            // Easy case: nothing provided by user, just create inference variables for everything.
1029            env.existential_substitution(id_span, &input_output.variables)
1030        }
1031
1032        Some(generics) => {
1033            // Harder case: user provided generics. Given that we are parsing a call like `a.b()`,
1034            // then generic arguments would be `a.b[x, y]()` and therefore correspond to the generics declared on
1035            // the function itself. But the `input_output` binder can contain additional parameters from
1036            // the class or surrounding scope. Those add'l parameters are instantiated with inference
1037            // variables-- and then the user-provided generics are added afterwards.
1038
1039            // Create existential substitution for any other generics.
1040            let function_generics = &signature.symbols(db).generic_variables;
1041            assert!(input_output.variables.ends_with(function_generics));
1042            let outer_variables =
1043                &input_output.variables[0..input_output.variables.len() - function_generics.len()];
1044            let mut substitution: Vec<SymGenericTerm<'_>> =
1045                env.existential_substitution(id_span, outer_variables);
1046
1047            // Check the user gave the expected number of arguments.
1048            if function_generics.len() != generics.len() {
1049                return ExprResult::err(
1050                    db,
1051                    env.report(
1052                        Diagnostic::error(
1053                            db,
1054                            id_span,
1055                            format!(
1056                                "expected {expected} generic arguments, but found {found}",
1057                                expected = function_generics.len(),
1058                                found = generics.len()
1059                            ),
1060                        )
1061                        .label(
1062                            db,
1063                            Level::Error,
1064                            id_span,
1065                            format!(
1066                                "{found} generic arguments were provided",
1067                                found = generics.len()
1068                            ),
1069                        )
1070                        .label(
1071                            db,
1072                            Level::Error,
1073                            function.name_span(db),
1074                            format!(
1075                                "the function `{name}` is declared with {expected} generic arguments",
1076                                name = function.name(db),
1077                                expected = function_generics.len(),
1078                            ),
1079                        ),
1080                    ),
1081                );
1082            }
1083
1084            // Convert each generic to a `SymGenericTerm` and check it has the correct kind.
1085            // If everything looks good, add it to the substitution.
1086            for (&ast_generic_term, &var) in generics.iter().zip(function_generics.iter()) {
1087                let generic_term = ast_generic_term.check_in_env(env).await;
1088                if !generic_term.has_kind(db, var.kind(db)) {
1089                    return ExprResult::err(
1090                        db,
1091                        env.report(
1092                            Diagnostic::error(
1093                                db,
1094                                ast_generic_term.span(db),
1095                                format!(
1096                                    "expected `{expected_kind}`, found `{found_kind}`",
1097                                    expected_kind = var.kind(db),
1098                                    found_kind = generic_term.kind().unwrap(),
1099                                ),
1100                            )
1101                            .label(
1102                                db,
1103                                Level::Error,
1104                                id_span,
1105                                format!(
1106                                    "this is a `{found_kind}`",
1107                                    found_kind = generic_term.kind().unwrap(),
1108                                ),
1109                            )
1110                            .label(
1111                                db,
1112                                Level::Info,
1113                                var.span(db),
1114                                format!(
1115                                    "I expected to find a `{expected_kind}`",
1116                                    expected_kind = var.kind(db),
1117                                ),
1118                            ),
1119                        ),
1120                    );
1121                }
1122                substitution.push(generic_term);
1123            }
1124
1125            substitution
1126        }
1127    };
1128
1129    check_call_common(
1130        env,
1131        live_after,
1132        function,
1133        expr_span,
1134        id_span,
1135        input_output,
1136        substitution,
1137        ast_args,
1138        self_expr,
1139        temporaries,
1140    )
1141    .await
1142}
1143
1144#[allow(clippy::too_many_arguments)]
1145#[boxed_async_fn]
1146async fn check_call_common<'db>(
1147    env: &mut Env<'db>,
1148    _live_after: LivePlaces,
1149    function: SymFunction<'db>,
1150    expr_span: Span<'db>,
1151    callee_span: Span<'db>,
1152    input_output: &Binder<'db, Binder<'db, SymInputOutput<'db>>>,
1153    substitution: Vec<SymGenericTerm<'db>>,
1154    ast_args: &[AstExpr<'db>],
1155    self_expr: Option<SymExpr<'db>>,
1156    mut temporaries: Vec<Temporary<'db>>,
1157) -> ExprResult<'db> {
1158    let db = env.db();
1159
1160    env.log("check_call_common", &[]);
1161    env.log("substitution", &[&substitution]);
1162
1163    // Instantiate the input-output with the substitution.
1164    let input_output = input_output.substitute(db, &substitution);
1165
1166    // Check the arity of the actual arguments.
1167    let self_args: usize = self_expr.is_some() as usize;
1168    let expected_inputs = input_output.bound_value.input_tys.len();
1169    let found_inputs = self_args + ast_args.len();
1170    if found_inputs != expected_inputs {
1171        let function_name = function.name(db);
1172        return ExprResult::err(
1173            db,
1174            env.report(
1175                Diagnostic::error(
1176                    db,
1177                    callee_span,
1178                    format!("expected {expected_inputs} arguments, found {found_inputs}"),
1179                )
1180                .label(
1181                    db,
1182                    Level::Error,
1183                    callee_span,
1184                    format!("I expected `{function_name}` to take {expected_inputs} arguments but I found {found_inputs}",),
1185                )
1186                .label(
1187                    db,
1188                    Level::Info,
1189                    function.name_span(db),
1190                    format!("`{function_name}` defined here"),
1191                )
1192            ),
1193        );
1194    }
1195
1196    // Create the temporaries that will hold the values for each argument.
1197    let arg_temp_span = |i: usize| {
1198        if i < self_args {
1199            self_expr.unwrap().span(db)
1200        } else {
1201            ast_args
1202                .get(i - self_args)
1203                .map(|a| a.span)
1204                .unwrap_or(callee_span)
1205        }
1206    };
1207    let arg_temp_symbols = (0..expected_inputs)
1208        .map(|i| SymVariable::new(db, SymGenericKind::Place, None, arg_temp_span(i)))
1209        .collect::<Vec<_>>();
1210    let arg_temp_terms = arg_temp_symbols
1211        .iter()
1212        .map(|&sym| SymGenericTerm::var(db, sym))
1213        .collect::<Vec<_>>();
1214
1215    // Instantiate the final level of binding with those temporaries
1216    let input_output: SymInputOutput<'_> = input_output.substitute(db, &arg_temp_terms);
1217
1218    env.log("arg_temp_symbols", &[&arg_temp_symbols]);
1219    env.log("arg_temp_terms", &[&arg_temp_terms]);
1220    env.log("input_output", &[&input_output]);
1221
1222    // Function to type check a single argument and check it has the correct type.
1223    let check_arg = async |i: usize| -> ExprResult<'db> {
1224        let mut env = env.fork(|log| log.spawn(Location::caller(), TaskDescription::CheckArg(i)));
1225        let mut arg_temporaries = vec![];
1226        let expr = if i < self_args {
1227            self_expr.unwrap()
1228        } else {
1229            let ast_arg = &ast_args[i - self_args];
1230            ast_arg
1231                .check_in_env(&mut env, LivePlaces::fixme())
1232                .await
1233                .into_expr(&mut env, &mut arg_temporaries)
1234        };
1235        env.spawn_require_assignable_type(
1236            LivePlaces::fixme(),
1237            expr.ty(db),
1238            input_output.input_tys[i],
1239            &BadSubtermError::new(expr.span(db), expr.ty(db), input_output.input_tys[i]),
1240        );
1241        ExprResult::from_expr(env.db(), expr, arg_temporaries)
1242    };
1243
1244    // Spawn out work to check the predicates.
1245    for where_clause in input_output.where_clauses {
1246        env.spawn_require_where_clause(
1247            where_clause,
1248            &WhereClauseError::new(callee_span, where_clause),
1249        );
1250    }
1251
1252    // Type check the arguments; these can proceed concurrently.
1253    let mut arg_exprs = vec![];
1254    arg_exprs.extend(self_expr);
1255    for arg_result in futures::future::join_all((0..found_inputs).map(check_arg)).await {
1256        arg_exprs.push(arg_result.into_expr(env, &mut temporaries));
1257    }
1258
1259    // Create the resulting call, which always looks like
1260    //
1261    //     let tmp1 = arg1 in
1262    //     let tmp2 = arg2 in
1263    //     ...
1264    //     call(tmp1, tmp2, ...)
1265    let mut call_expr = SymExpr::new(
1266        db,
1267        expr_span,
1268        input_output.output_ty,
1269        SymExprKind::Call {
1270            function,
1271            substitution,
1272            arg_temps: arg_temp_symbols.clone(),
1273        },
1274    );
1275    for (arg_temp_symbol, arg_expr) in arg_temp_symbols
1276        .into_iter()
1277        .rev()
1278        .zip(arg_exprs.into_iter().rev())
1279    {
1280        call_expr = SymExpr::new(
1281            db,
1282            call_expr.span(db),
1283            call_expr.ty(db),
1284            SymExprKind::LetIn {
1285                lv: arg_temp_symbol,
1286                ty: arg_expr.ty(db),
1287                initializer: Some(arg_expr),
1288                body: call_expr,
1289            },
1290        );
1291    }
1292
1293    // Create the final result.
1294    ExprResult::from_expr(env.db(), call_expr, temporaries)
1295}
1296
1297impl<'db> Err<'db> for ExprResult<'db> {
1298    fn err(db: &'db dyn dada_ir_ast::Db, r: Reported) -> Self {
1299        Self {
1300            temporaries: vec![],
1301            span: r.span(db),
1302            kind: ExprResultKind::PlaceExpr(SymPlaceExpr::err(db, r)),
1303        }
1304    }
1305}
1306
1307impl<'db> ExprResult<'db> {
1308    /// Create a result based on lexical name resolution.
1309    pub async fn from_name_resolution(
1310        env: &mut Env<'db>,
1311        res: NameResolution<'db>,
1312        span: Span<'db>,
1313    ) -> Self {
1314        let db = env.db();
1315        match res.sym {
1316            NameResolutionSym::SymVariable(var) if var.kind(db) == SymGenericKind::Place => {
1317                let ty = env.variable_ty(var).await;
1318                let place_expr = SymPlaceExpr::new(db, span, ty, SymPlaceExprKind::Var(var));
1319                Self {
1320                    temporaries: vec![],
1321                    span,
1322                    kind: ExprResultKind::PlaceExpr(place_expr),
1323                }
1324            }
1325
1326            // FIXME: Should functions be expressions?
1327            NameResolutionSym::SymFunction(_)
1328            | NameResolutionSym::SymModule(_)
1329            | NameResolutionSym::SymAggregate(_)
1330            | NameResolutionSym::SymPrimitive(_)
1331            | NameResolutionSym::SymVariable(..) => Self {
1332                temporaries: vec![],
1333                span,
1334                kind: ExprResultKind::Other(res),
1335            },
1336        }
1337    }
1338
1339    pub fn from_place_expr(
1340        db: &'db dyn crate::Db,
1341        expr: SymPlaceExpr<'db>,
1342        temporaries: Vec<Temporary<'db>>,
1343    ) -> Self {
1344        Self {
1345            temporaries,
1346            span: expr.span(db),
1347            kind: ExprResultKind::PlaceExpr(expr),
1348        }
1349    }
1350
1351    pub fn from_expr(
1352        db: &'db dyn crate::Db,
1353        expr: SymExpr<'db>,
1354        temporaries: Vec<Temporary<'db>>,
1355    ) -> Self {
1356        Self {
1357            temporaries,
1358            span: expr.span(db),
1359            kind: ExprResultKind::Expr(expr),
1360        }
1361    }
1362
1363    /// Convert this result into an expression, with `let ... in` statements inserted for temporaries.
1364    pub fn into_expr_with_enclosed_temporaries(self, env: &mut Env<'db>) -> SymExpr<'db> {
1365        let db = env.db();
1366        let mut temporaries = vec![];
1367        let mut expr = self.into_expr(env, &mut temporaries);
1368        for temporary in temporaries.into_iter().rev() {
1369            expr = SymExpr::new(
1370                db,
1371                expr.span(db),
1372                expr.ty(db),
1373                SymExprKind::LetIn {
1374                    lv: temporary.lv,
1375                    ty: temporary.ty,
1376                    initializer: temporary.initializer,
1377                    body: expr,
1378                },
1379            );
1380        }
1381
1382        expr
1383    }
1384
1385    /// Computes the type of this, treating it as an expression.
1386    /// Reports an error if this names something that cannot be made into an expression.
1387    pub fn ty(&self, env: &mut Env<'db>) -> SymTy<'db> {
1388        let db = env.db();
1389        match &self.kind {
1390            &ExprResultKind::PlaceExpr(place_expr) => place_expr.ty(db),
1391            &ExprResultKind::Expr(expr) => expr.ty(db),
1392            ExprResultKind::Other(name_resolution) => {
1393                SymTy::err(db, report_non_expr(db, self.span, name_resolution))
1394            }
1395            &ExprResultKind::Method {
1396                self_expr: owner,
1397                function: method,
1398                ..
1399            } => SymTy::err(
1400                db,
1401                report_missing_call_to_method(db, owner.span(db), method),
1402            ),
1403        }
1404    }
1405
1406    pub fn into_place_expr(
1407        self,
1408        env: &mut Env<'db>,
1409        temporaries: &mut Vec<Temporary<'db>>,
1410    ) -> SymPlaceExpr<'db> {
1411        let db = env.db();
1412        temporaries.extend(self.temporaries);
1413        match self.kind {
1414            ExprResultKind::PlaceExpr(place_expr) => place_expr,
1415
1416            // This is a value that needs to be stored in a temporary.
1417            ExprResultKind::Expr(expr) => expr.into_temporary(db, temporaries),
1418
1419            ExprResultKind::Other(name_resolution) => {
1420                let reported = report_non_expr(db, self.span, &name_resolution);
1421                SymPlaceExpr::err(db, reported)
1422            }
1423
1424            ExprResultKind::Method {
1425                self_expr: owner,
1426                function: method,
1427                ..
1428            } => SymPlaceExpr::err(
1429                db,
1430                report_missing_call_to_method(db, owner.span(db), method),
1431            ),
1432        }
1433    }
1434
1435    pub fn into_expr(
1436        self,
1437        env: &mut Env<'db>,
1438        temporaries: &mut Vec<Temporary<'db>>,
1439    ) -> SymExpr<'db> {
1440        let db = env.db();
1441        temporaries.extend(self.temporaries);
1442        match self.kind {
1443            ExprResultKind::Expr(expr) => expr,
1444            ExprResultKind::PlaceExpr(place_expr) => {
1445                let sym_place = place_expr.into_sym_place(db);
1446                SymExpr::new(
1447                    db,
1448                    place_expr.span(db),
1449                    place_expr.ty(db).referenced(db, sym_place),
1450                    SymExprKind::PermissionOp(PermissionOp::Reference, place_expr),
1451                )
1452            }
1453
1454            ExprResultKind::Other(name_resolution) => {
1455                SymExpr::err(db, report_non_expr(db, self.span, &name_resolution))
1456            }
1457            ExprResultKind::Method {
1458                self_expr: owner,
1459                function: method,
1460                ..
1461            } => SymExpr::err(
1462                db,
1463                report_missing_call_to_method(db, owner.span(db), method),
1464            ),
1465        }
1466    }
1467}
1468
1469fn report_not_implemented<'db>(db: &'db dyn crate::Db, span: Span<'db>, what: &str) -> Reported {
1470    Diagnostic::error(db, span, "not implemented yet :(".to_string())
1471        .label(
1472            db,
1473            Level::Error,
1474            span,
1475            format!("sorry, but {what} have not been implemented yet :(",),
1476        )
1477        .report(db)
1478}
1479
1480fn report_non_expr<'db>(
1481    db: &'db dyn crate::Db,
1482    owner_span: Span<'db>,
1483    name_resolution: &NameResolution<'db>,
1484) -> Reported {
1485    Diagnostic::error(db, owner_span, "expected an expression".to_string())
1486        .label(
1487            db,
1488            Level::Error,
1489            owner_span,
1490            format!(
1491                "I expected to find an expression but I found {}",
1492                name_resolution.categorize(db),
1493            ),
1494        )
1495        .report(db)
1496}
1497
1498fn report_missing_call_to_method<'db>(
1499    db: &'db dyn crate::Db,
1500    owner_span: Span<'db>,
1501    method: SymFunction<'db>,
1502) -> Reported {
1503    Diagnostic::error(db, owner_span, "missing call to method".to_string())
1504        .label(
1505            db,
1506            Level::Error,
1507            owner_span,
1508            format!(
1509                "`{}` is a method but you don't appear to be calling it",
1510                method.name(db),
1511            ),
1512        )
1513        .label(db, Level::Help, owner_span.at_end(), "maybe add `()` here?")
1514        .report(db)
1515}
1516
1517fn report_not_callable<'db>(db: &'db dyn crate::Db, owner_span: Span<'db>) -> Reported {
1518    Diagnostic::error(db, owner_span, "not callable".to_string())
1519        .label(
1520            db,
1521            Level::Error,
1522            owner_span,
1523            "this is not something you can call".to_string(),
1524        )
1525        .report(db)
1526}