pub fn check_term(
    context: &Context,
    concrete_term: &Term<'_>,
    expected_ty: &domain::RcType,
) -> Result<core::RcTerm, Diagnostic<FileSpan>> {
    log::trace!("checking term:\t\t{}", concrete_term);

    match concrete_term {
        Term::Parens(_, concrete_term) => check_term(context, concrete_term, expected_ty),
        Term::Let(_, concrete_items, concrete_body) => {
            let mut context = context.clone();
            let items = check_items(&mut context, concrete_items)?;
            let body = check_term(&context, concrete_body, expected_ty)?;

            Ok(items.into_iter().rev().fold(body, |acc, item| {
                // TODO: other item fields?
                core::RcTerm::from(core::Term::Let(item.term, acc))
            }))
        },
        Term::If(_, condition, consequent, alternative) => {
            let bool_ty = domain::RcValue::from(domain::Value::LiteralType(LiteralType::Bool));
            let condition = check_term(context, condition, &bool_ty)?;
            let consequent = check_term(context, consequent, expected_ty)?;
            let alternative = {
                let mut inner_context = context.clone();
                inner_context.local_bind(None, bool_ty);
                check_term(&inner_context, alternative, expected_ty)?
            };

            Ok(core::RcTerm::from(core::Term::LiteralElim(
                condition,
                Rc::from(vec![(LiteralIntro::Bool(true), consequent)]),
                alternative,
            )))
        },

        Term::Literal(concrete_literal) => literal::check(context, concrete_literal, expected_ty),

        Term::FunIntro(_, concrete_params, concrete_body) => {
            let clause = Clause::new(concrete_params, None, concrete_body);
            clause::check_clause(context, clause, expected_ty)
        },

        Term::RecordIntro(span, concrete_intro_fields) => {
            let mut context = context.clone();
            let mut fields = Vec::new();
            let mut expected_ty = expected_ty.clone();

            for concrete_intro_field in concrete_intro_fields {
                let (expected_label, expected_term_ty, rest) = match expected_ty.as_ref() {
                    domain::Value::RecordTypeExtend(_, label, ty, rest) => Ok((label, ty, rest)),
                    _ => Err(Diagnostic::new_error("too many fields found")
                        .with_label(DiagnosticLabel::new_primary(*span))),
                }?;

                let (found_label, params, body_ty, body) = concrete_intro_field.desugar();

                if found_label.slice == expected_label.0 {
                    let clause = Clause::new(params, body_ty, &body);
                    let term = clause::check_clause(&context, clause, expected_term_ty)?;

                    let term_value = context.eval(body.span(), &term)?;
                    let term_ty = expected_term_ty.clone();

                    fields.push((expected_label.clone(), term));
                    context.local_define(found_label.to_string(), term_value.clone(), term_ty);
                    expected_ty = do_closure_app(&rest, term_value)?;
                } else {
                    return Err(Diagnostic::new_error("field not found").with_label(
                        DiagnosticLabel::new_primary(found_label.span()).with_message(format!(
                            "expected `{}`, but found `{}`",
                            found_label, expected_label,
                        )),
                    ));
                }
            }

            if let domain::Value::RecordTypeEmpty = expected_ty.as_ref() {
                Ok(core::RcTerm::from(core::Term::RecordIntro(fields)))
            } else {
                Err(Diagnostic::new_error("not enough fields provided")
                    .with_label(DiagnosticLabel::new_primary(*span)))
            }
        },

        _ => {
            let (synth, synth_ty) = synth_term(context, concrete_term)?;
            context.expect_subtype(concrete_term.span(), &synth_ty, expected_ty)?;
            Ok(synth)
        },
    }
}
/// Check that a given term conforms to an expected type.
///
/// Returns the elaborated term.
Check that a given term conforms to an expected type.
Returns the elaborated term.
Check that a given term conforms to an expected type . Returns the elaborated term .
pub fn check_term(
    context: &Context,
    concrete_term: &Term<'_>,
    expected_ty: &domain::RcType,
) -> Result<core::RcTerm, Diagnostic<FileSpan>> {
    log::trace!("checking term:\t\t{}", concrete_term);
    match concrete_term {
        Term::Parens(_, concrete_term) => check_term(context, concrete_term, expected_ty),
        Term::Let(_, concrete_items, concrete_body) => {
            let mut context = context.clone();
            let items = check_items(&mut context, concrete_items)?;
            let body = check_term(&context, concrete_body, expected_ty)?;
            Ok(items.into_iter().rev().fold(body, |acc, item| {
                // TODO: other item fields?
                core::RcTerm::from(core::Term::Let(item.term, acc))
            }))
        },
        Term::If(_, condition, consequent, alternative) => {
            let bool_ty = domain::RcValue::from(domain::Value::LiteralType(LiteralType::Bool));
            let condition = check_term(context, condition, &bool_ty)?;
            let consequent = check_term(context, consequent, expected_ty)?;
            let alternative = {
                let mut inner_context = context.clone();
                inner_context.local_bind(None, bool_ty);
                check_term(&inner_context, alternative, expected_ty)?
            };
            Ok(core::RcTerm::from(core::Term::LiteralElim(
                condition,
                Rc::from(vec![(LiteralIntro::Bool(true), consequent)]),
                alternative,
            )))
        },
        Term::Literal(concrete_literal) => literal::check(context, concrete_literal, expected_ty),
        Term::FunIntro(_, concrete_params, concrete_body) => {
            let clause = Clause::new(concrete_params, None, concrete_body);
            clause::check_clause(context, clause, expected_ty)
        },
        Term::RecordIntro(span, concrete_intro_fields) => {
            let mut context = context.clone();
            let mut fields = Vec::new();
            let mut expected_ty = expected_ty.clone();
            for concrete_intro_field in concrete_intro_fields {
                let (expected_label, expected_term_ty, rest) = match expected_ty.as_ref() {
                    domain::Value::RecordTypeExtend(_, label, ty, rest) => Ok((label, ty, rest)),
                    _ => Err(Diagnostic::new_error("too many fields found")
                        .with_label(DiagnosticLabel::new_primary(*span))),
                }?;
                let (found_label, params, body_ty, body) = concrete_intro_field.desugar();
                if found_label.slice == expected_label.0 {
                    let clause = Clause::new(params, body_ty, &body);
                    let term = clause::check_clause(&context, clause, expected_term_ty)?;
                    let term_value = context.eval(body.span(), &term)?;
                    let term_ty = expected_term_ty.clone();
                    fields.push((expected_label.clone(), term));
                    context.local_define(found_label.to_string(), term_value.clone(), term_ty);
                    expected_ty = do_closure_app(&rest, term_value)?;
                } else {
                    return Err(Diagnostic::new_error("field not found").with_label(
                        DiagnosticLabel::new_primary(found_label.span()).with_message(format!(
                            "expected `{}`, but found `{}`",
                            found_label, expected_label,
                        )),
                    ));
                }
            }
            if let domain::Value::RecordTypeEmpty = expected_ty.as_ref() {
                Ok(core::RcTerm::from(core::Term::RecordIntro(fields)))
            } else {
                Err(Diagnostic::new_error("not enough fields provided")
                    .with_label(DiagnosticLabel::new_primary(*span)))
            }
        },
        _ => {
            let (synth, synth_ty) = synth_term(context, concrete_term)?;
            context.expect_subtype(concrete_term.span(), &synth_ty, expected_ty)?;
            Ok(synth)
        },
    }
}
pub fn check_term ( context : & Context , concrete_term : & Term < ' _ > , expected_ty : & domain :: RcType , ) -> Result < core :: RcTerm , Diagnostic < FileSpan > > { log :: trace ! ( " \t \t " concrete_term ) ; match concrete_term { Term :: Parens ( _ , concrete_term ) => check_term ( context , concrete_term , expected_ty ) , Term :: Let ( _ , concrete_items , concrete_body ) => { let mut context = context . clone ( ) ; let items = check_items ( & mut context , concrete_items ) ? ; let body = check_term ( & context , concrete_body , expected_ty ) ? ; Ok ( items . into_iter ( ) . rev ( ) . fold ( body , | acc , item | { // TODO: other item fields? core :: RcTerm :: from ( core :: Term :: Let ( item . term , acc ) ) } ) ) } , Term :: If ( _ , condition , consequent , alternative ) => { let bool_ty = domain :: RcValue :: from ( domain :: Value :: LiteralType ( LiteralType :: Bool ) ) ; let condition = check_term ( context , condition , & bool_ty ) ? ; let consequent = check_term ( context , consequent , expected_ty ) ? ; let alternative = { let mut inner_context = context . clone ( ) ; inner_context . local_bind ( None , bool_ty ) ; check_term ( & inner_context , alternative , expected_ty ) ? } ; Ok ( core :: RcTerm :: from ( core :: Term :: LiteralElim ( condition , Rc :: from ( vec ! [ ( LiteralIntro Bool ( true ) consequent ) ] ) , alternative , ) ) ) } , Term :: Literal ( concrete_literal ) => literal :: check ( context , concrete_literal , expected_ty ) , Term :: FunIntro ( _ , concrete_params , concrete_body ) => { let clause = Clause :: new ( concrete_params , None , concrete_body ) ; clause :: check_clause ( context , clause , expected_ty ) } , Term :: RecordIntro ( span , concrete_intro_fields ) => { let mut context = context . clone ( ) ; let mut fields = Vec :: new ( ) ; let mut expected_ty = expected_ty . clone ( ) ; for concrete_intro_field in concrete_intro_fields { let ( expected_label , expected_term_ty , rest ) = match expected_ty . as_ref ( ) { domain :: Value :: RecordTypeExtend ( _ , label , ty , rest ) => Ok ( ( label , ty , rest ) ) , _ => Err ( Diagnostic :: new_error ( " " ) . with_label ( DiagnosticLabel :: new_primary ( * span ) ) ) , } ? ; let ( found_label , params , body_ty , body ) = concrete_intro_field . desugar ( ) ; if found_label . slice == expected_label . 0 { let clause = Clause :: new ( params , body_ty , & body ) ; let term = clause :: check_clause ( & context , clause , expected_term_ty ) ? ; let term_value = context . eval ( body . span ( ) , & term ) ? ; let term_ty = expected_term_ty . clone ( ) ; fields . push ( ( expected_label . clone ( ) , term ) ) ; context . local_define ( found_label . to_string ( ) , term_value . clone ( ) , term_ty ) ; expected_ty = do_closure_app ( & rest , term_value ) ? ; } else { return Err ( Diagnostic :: new_error ( " " ) . with_label ( DiagnosticLabel :: new_primary ( found_label . span ( ) ) . with_message ( format ! ( " " found_label expected_label ) ) , ) ) ; } } if let domain :: Value :: RecordTypeEmpty = expected_ty . as_ref ( ) { Ok ( core :: RcTerm :: from ( core :: Term :: RecordIntro ( fields ) ) ) } else { Err ( Diagnostic :: new_error ( " " ) . with_label ( DiagnosticLabel :: new_primary ( * span ) ) ) } } , _ => { let ( synth , synth_ty ) = synth_term ( context , concrete_term ) ? ; context . expect_subtype ( concrete_term . span ( ) , & synth_ty , expected_ty ) ? ; Ok ( synth ) } , } }
Check that a given term conforms to an expected type.
Check that a given term conforms to an expected type .
context
&Context
concrete_term
&Term<'_>
expected_ty
&domain::RcType