Something went wrong. Try again.
An experiment in static, refcounted, procedural language with ownership
Something went wrong. Try again.
3.6 kB · 100 lines
Rust
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101use compiler_core::diagnostics::{Diagnostic, Label};use compiler_core::hir;use compiler_core::span::Spanned;use compiler_core::{ db::{DefId, Hook}, diagnostics::DiagnosticResult, middle::Mutability, ty::TyKind,};
use crate::Typeck;
pub(crate) fn check(cx: &mut Typeck, sig: &hir::FnSig, id: DefId) -> DiagnosticResult<()> { let Ok(hook) = Hook::try_from(sig.word.as_str()) else { return Ok(()); };
match hook { Hook::Drop => check_drop_hook(cx, sig, id), }}
fn check_drop_hook(cx: &mut Typeck, sig: &hir::FnSig, id: DefId) -> DiagnosticResult<()> { let fn_ty = sig.ty.as_fn().unwrap();
let param = if fn_ty.params.len() == 1 { &fn_ty.params[0] } else { return Err(Diagnostic::error("drop hook must have exactly 1 parameter").with_label( Label::primary(sig.word.span(), format!("has {} parameters", fn_ty.params.len())), )); };
let (adt_id, targs) = match param.ty.kind() { TyKind::Ref(ty, Mutability::Mut) => match ty.kind() { TyKind::Adt(adt_id, targs) => (*adt_id, targs), ty => { return Err(Diagnostic::error(format!( "cannot define drop hook for type `{}`", ty.display(cx.db) )) .with_label(Label::primary(sig.params[0].pat.span(), "invalid parameter type"))) } }, _ => { return Err(Diagnostic::error("parameter must be a &mut reference") .with_label(Label::primary(sig.params[0].pat.span(), "invalid parameter type"))) } };
if !cx.db[adt_id].is_ref() { return Err(Diagnostic::error(format!( "cannot define drop hook for value type `{}`", cx.db[adt_id].name )) .with_label(Label::primary(sig.params[0].pat.span(), "parameter is a value type"))); }
let any_non_poly_targs = targs.iter().any(|ty| ty.walk_short(|ty| !matches!(ty.kind(), TyKind::Param(_)))); if any_non_poly_targs { return Err(Diagnostic::error("drop hook cannot be specialized") .with_label(Label::primary( sig.params[0].pat.span(), "has non-polymorphic type arguments", )) .with_note("change parameter type to only contain polymorphic type arguments")); }
if !fn_ty.ret.is_unit() { return Err(Diagnostic::error("drop hook must return `()`") .with_label(Label::primary(sig.ret_span, "invalid return type"))); }
let hook_pkg = cx.db[cx.db[id].scope.module_id].package; let adt_pkg = cx.db[cx.db[cx.db[adt_id].def_id].scope.module_id].package;
if hook_pkg != adt_pkg { return Err(Diagnostic::error(format!( "cannot define drop hook for type `{}` that is defined outside this package", cx.db[adt_id].name, )) .with_label(Label::primary(sig.word.span(), "cannot define hook for foreign type")) .with_note(format!( "type `{}` is defined outside this package, in package `{}`", cx.db[adt_id].name, adt_pkg ))); }
if let Some(prev_id) = cx.db.hooks.insert((adt_id, Hook::Drop), id) { return Err(Diagnostic::error(format!( "drop hook is already defined for type `{}`", cx.db[adt_id].name, )) .with_label(Label::primary(sig.word.span(), "defined here")) .with_label(Label::secondary(cx.db[prev_id].span, "also defined here"))); }
Ok(())}