Skip to content

Commit

Permalink
[WIP]
Browse files Browse the repository at this point in the history
  • Loading branch information
Y-Nak committed Apr 18, 2024
1 parent b609ab7 commit e0585df
Show file tree
Hide file tree
Showing 16 changed files with 220 additions and 40 deletions.
4 changes: 2 additions & 2 deletions crates/hir-analysis/src/ty/constraint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::{
binder::Binder,
func_def::HirFuncDefKind,
trait_lower::{lower_impl_trait, lower_trait},
ty_def::{free_inference_keys, TyVarSort},
ty_def::{inference_keys, TyVarSort},
unify::InferenceKey,
},
HirAnalysisDb,
Expand Down Expand Up @@ -58,7 +58,7 @@ pub(crate) fn ty_constraints(db: &dyn HirAnalysisDb, ty: TyId) -> ConstraintList
// If the constraint type contains unbound type parameters, we just ignore it.
let mut new_constraints = BTreeSet::new();
for &pred in constraints.predicates(db) {
if free_inference_keys(db, pred.ty(db)).is_empty() {
if inference_keys(db, pred.ty(db)).is_empty() {
new_constraints.insert(pred);
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/hir-analysis/src/ty/constraint_solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use super::{
use crate::{
ty::{
constraint::{collect_super_traits, ty_constraints},
ty_def::free_inference_keys,
ty_def::inference_keys,
},
HirAnalysisDb,
};
Expand All @@ -33,7 +33,7 @@ pub(crate) fn check_ty_wf(
ty: TyId,
assumptions: AssumptionListId,
) -> GoalSatisfiability {
assert!(free_inference_keys(db, ty).is_empty());
assert!(inference_keys(db, ty).is_empty());

let (_, args) = ty.decompose_ty_app(db);

Expand Down
3 changes: 3 additions & 0 deletions crates/hir-analysis/src/ty/def_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,9 @@ impl<'db> Visitor for DefAnalyzer<'db> {
self.def = def;
}

fn visit_body(&mut self, _ctxt: &mut VisitorCtxt<'_, LazyBodySpan>, _body: hir::hir_def::Body) {
}

fn visit_func_param_list(
&mut self,
ctxt: &mut VisitorCtxt<'_, LazyFuncParamListSpan>,
Expand Down
21 changes: 21 additions & 0 deletions crates/hir-analysis/src/ty/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,10 @@ pub enum BodyDiag {
primary: DynLazySpan,
given: Either<ItemKind, TyId>,
},

TypeAnnotationNeeded {
primary: DynLazySpan,
},
}

impl BodyDiag {
Expand Down Expand Up @@ -885,6 +889,7 @@ impl BodyDiag {
Self::InvisibleTraitMethod { .. } => 27,
Self::MethodNotFound { .. } => 28,
Self::NotValue { .. } => 29,
Self::TypeAnnotationNeeded { .. } => 30,
}
}

Expand Down Expand Up @@ -947,6 +952,7 @@ impl BodyDiag {
}

Self::NotValue { .. } => "value is expected".to_string(),
Self::TypeAnnotationNeeded { .. } => "type annotation is needed".to_string(),
}
}

Expand Down Expand Up @@ -1482,6 +1488,21 @@ impl BodyDiag {
primary.resolve(db),
)]
}

Self::TypeAnnotationNeeded { primary } => {
vec![
SubDiagnostic::new(
LabelStyle::Primary,
"type annotation is needed".to_string(),
primary.resolve(db),
),
SubDiagnostic::new(
LabelStyle::Secondary,
"consider giving `: Type` here".to_string(),
primary.resolve(db),
),
]
}
}
}

Expand Down
8 changes: 4 additions & 4 deletions crates/hir-analysis/src/ty/ty_check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -671,13 +671,13 @@ impl<'db> TyChecker<'db> {
unreachable!()
};

let expected_elem_ty = match expected.decompose_ty_app(self.db) {
let mut expected_elem_ty = match expected.decompose_ty_app(self.db) {
(base, args) if base.is_array(self.db) => args[0],
_ => self.fresh_ty(),
};

for elem in elems {
self.check_expr(*elem, expected_elem_ty);
expected_elem_ty = self.check_expr(*elem, expected_elem_ty).ty;
}

let ty = TyId::array_with_elem(self.db, expected_elem_ty, elems.len());
Expand All @@ -689,12 +689,12 @@ impl<'db> TyChecker<'db> {
unreachable!()
};

let expected_elem_ty = match expected.decompose_ty_app(self.db) {
let mut expected_elem_ty = match expected.decompose_ty_app(self.db) {
(base, args) if base.is_array(self.db) => args[0],
_ => self.fresh_ty(),
};

self.check_expr(*elem, expected_elem_ty);
expected_elem_ty = self.check_expr(*elem, expected_elem_ty).ty;

let array = TyId::app(self.db, TyId::array(self.db), expected_elem_ty);
let ty = if let Some(len_body) = len.to_opt() {
Expand Down
121 changes: 112 additions & 9 deletions crates/hir-analysis/src/ty/ty_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,27 @@ pub use env::ExprProp;
use env::TyCheckEnv;
pub(super) use expr::TraitOps;
use hir::{
hir_def::{Body, ExprId, Func, LitKind, PatId, TypeId as HirTyId},
span::DynLazySpan,
hir_def::{Body, Expr, ExprId, Func, LitKind, Pat, PatId, TypeId as HirTyId},
span::{expr::LazyExprSpan, pat::LazyPatSpan, DynLazySpan},
visitor::{walk_expr, walk_pat, Visitor, VisitorCtxt},
};
pub(super) use path::RecordLike;
use rustc_hash::FxHashMap;
use rustc_hash::{FxHashMap, FxHashSet};

use super::{
canonical::Canonical,
diagnostics::{BodyDiag, FuncBodyDiagAccumulator, TyDiagCollection, TyLowerDiag},
constraint::AssumptionListId,
diagnostics::{BodyDiag, FuncBodyDiag, FuncBodyDiagAccumulator, TyDiagCollection, TyLowerDiag},
fold::TyFoldable,
trait_def::{TraitInstId, TraitMethod},
ty_def::{InvalidCause, Kind, TyId, TyVarSort},
ty_lower::lower_hir_ty,
unify::{UnificationError, UnificationTable},
unify::{InferenceKey, UnificationError, UnificationTable},
};
use crate::{
ty::ty_def::{inference_keys, TyFlags},
HirAnalysisDb,
};
use crate::HirAnalysisDb;

#[salsa::tracked(return_ref)]
pub fn check_func_body(db: &dyn HirAnalysisDb, func: Func) -> TypedBody {
Expand Down Expand Up @@ -67,9 +72,8 @@ impl<'db> TyChecker<'db> {
self.check_expr(root_expr, self.expected);
}

fn finish(mut self) -> TypedBody {
// TODO: check for untyped expressions and patterns.
self.env.finish(&mut self.table)
fn finish(self) -> TypedBody {
TyCheckerFinalizer::new(self).finish()
}

fn new(db: &'db dyn HirAnalysisDb, env: TyCheckEnv<'db>, expected: TyId) -> Self {
Expand Down Expand Up @@ -247,3 +251,102 @@ impl TraitMethod {
tc.table.instantiate_to_term(ty)
}
}

struct TyCheckerFinalizer<'db> {
db: &'db dyn HirAnalysisDb,
body: TypedBody,
assumptions: AssumptionListId,
ty_vars: FxHashSet<InferenceKey>,
wf_reported: FxHashSet<TyId>,
diags: Vec<FuncBodyDiag>,
}

impl<'db> TyCheckerFinalizer<'db> {
fn new(mut checker: TyChecker<'db>) -> Self {
let assumptions = checker.env.assumptions();
let body = checker.env.finish(&mut checker.table);
Self {
db: checker.db,
body,
assumptions,
ty_vars: FxHashSet::default(),
wf_reported: FxHashSet::default(),
diags: Vec::new(),
}
}

fn finish(mut self) -> TypedBody {
self.check_unknown_types();

for diag in self.diags {
FuncBodyDiagAccumulator::push(self.db, diag);
}
self.body
}

fn check_unknown_types(&mut self) {
impl<'db> Visitor for TyCheckerFinalizer<'db> {
fn visit_pat(&mut self, ctxt: &mut VisitorCtxt<'_, LazyPatSpan>, pat: PatId, _: &Pat) {
let ty = self.body.pat_ty(self.db, pat);
let span = ctxt.span().unwrap();
self.check_unknown(ty, span.clone().into());
self.check_wf(ty, span.into());

walk_pat(self, ctxt, pat)
}

fn visit_expr(
&mut self,
ctxt: &mut VisitorCtxt<'_, LazyExprSpan>,
expr: ExprId,
_: &Expr,
) {
let ty = self.body.expr_ty(self.db, expr);
let span = ctxt.span().unwrap();
self.check_unknown(ty, span.clone().into());
self.check_wf(ty, span.into());

walk_expr(self, ctxt, expr)
}
}

if let Some(body) = self.body.body {
let mut ctxt = VisitorCtxt::with_body(self.db.as_hir_db(), body);
self.visit_body(&mut ctxt, body);
}
}

fn check_unknown(&mut self, ty: TyId, span: DynLazySpan) {
let flags = ty.flags(self.db);
if flags.contains(TyFlags::HAS_INVALID) || !flags.contains(TyFlags::HAS_VAR) {
return;
}

let mut skip_diag = false;
for key in inference_keys(self.db, ty) {
// If at least one of the inference keys are already seen, we will skip emitting
// diagnostics.
skip_diag |= !self.ty_vars.insert(key);
}

if !skip_diag {
let diag = BodyDiag::TypeAnnotationNeeded { primary: span };
self.diags.push(diag.into())
}
}

fn check_wf(&mut self, ty: TyId, span: DynLazySpan) {
let flags = ty.flags(self.db);
if flags.contains(TyFlags::HAS_INVALID)
|| flags.contains(TyFlags::HAS_VAR)
|| self.wf_reported.contains(&ty)
{
return;
}

if let Some(diag) = ty.emit_sat_diag(self.db, self.assumptions, span) {
self.wf_reported.insert(ty);
self.diags.push(diag.into());
}
}
}
11 changes: 6 additions & 5 deletions crates/hir-analysis/src/ty/ty_def.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! This module contains the type definitions for the Fe type system.
use std::{collections::BTreeSet, fmt};
use std::fmt;

use bitflags::bitflags;
use common::input::IngotKind;
Expand All @@ -12,6 +12,7 @@ use hir::{
},
span::DynLazySpan,
};
use rustc_hash::FxHashSet;

use super::{
adt_def::AdtDef,
Expand Down Expand Up @@ -974,16 +975,16 @@ impl HasKind for FuncDef {
}
}

pub(crate) fn free_inference_keys<'db, V>(
pub(crate) fn inference_keys<'db, V>(
db: &'db dyn HirAnalysisDb,
visitable: V,
) -> BTreeSet<InferenceKey>
) -> FxHashSet<InferenceKey>
where
V: TyVisitable<'db>,
{
struct FreeInferenceKeyCollector<'db> {
db: &'db dyn HirAnalysisDb,
keys: BTreeSet<InferenceKey>,
keys: FxHashSet<InferenceKey>,
}

impl<'db> TyVisitor<'db> for FreeInferenceKeyCollector<'db> {
Expand All @@ -998,7 +999,7 @@ where

let mut collector = FreeInferenceKeyCollector {
db,
keys: BTreeSet::new(),
keys: FxHashSet::default(),
};

visitable.visit_with(&mut collector);
Expand Down
4 changes: 2 additions & 2 deletions crates/hir-analysis/src/ty/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::{
binder::Binder,
fold::{TyFoldable, TyFolder},
trait_def::{Implementor, TraitInstId},
ty_def::{free_inference_keys, ApplicableTyProp, Kind, TyData, TyId, TyVar, TyVarSort},
ty_def::{inference_keys, ApplicableTyProp, Kind, TyData, TyId, TyVar, TyVarSort},
};
use crate::{
ty::const_ty::{ConstTyData, EvaluatedConstTy},
Expand Down Expand Up @@ -242,7 +242,7 @@ impl<'db> UnificationTable<'db> {
/// 2. Universe check: The sort of the type variable must match the sort of
/// the type.
fn unify_var_value(&mut self, var: &TyVar, value: TyId) -> UnificationResult {
if free_inference_keys(self.db, value).contains(&var.key) {
if inference_keys(self.db, value).contains(&var.key) {
return Err(UnificationError::OccursCheckFailed);
}

Expand Down
11 changes: 5 additions & 6 deletions crates/hir/src/hir_def/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@ use cranelift_entity::{EntityRef, PrimaryMap, SecondaryMap};
use parser::ast::{self, prelude::*};
use rustc_hash::FxHashMap;

use super::{
scope_graph::ScopeId, Expr, ExprId, Partial, Pat, PatId, Stmt, StmtId, TopLevelMod,
TrackedItemId,
};
use crate::{
span::{item::LazyBodySpan, HirOrigin},
visitor::prelude::*,
HirDb,
};

use super::{
scope_graph::ScopeId, Expr, ExprId, Partial, Pat, PatId, Stmt, StmtId, TopLevelMod,
TrackedItemId,
};

#[salsa::tracked]
pub struct Body {
#[id]
Expand Down Expand Up @@ -72,7 +71,7 @@ impl Body {
/// Currently, this is only used for testing.
/// When it turns out to be generally useful, we need to consider to let
/// salsa track this method.
pub fn block_order(self, db: &dyn HirDb) -> FxHashMap<ExprId, usize> {
pub fn iter_block(self, db: &dyn HirDb) -> FxHashMap<ExprId, usize> {
BlockOrderCalculator::new(db, self).calculate()
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/hir/src/hir_def/scope_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ impl ScopeId {
}
pub fn pretty_path(self, db: &dyn HirDb) -> Option<String> {
let name = match self {
ScopeId::Block(body, expr) => format!("{{block{}}}", body.block_order(db)[&expr]),
ScopeId::Block(body, expr) => format!("{{block{}}}", body.iter_block(db)[&expr]),
ScopeId::Item(ItemKind::Body(body)) => match body.body_kind(db) {
BodyKind::FuncBody => "{fn_body}".to_string(),
BodyKind::Anonymous => "{anonymous_body}".to_string(),
Expand Down
5 changes: 2 additions & 3 deletions crates/hir/src/hir_def/scope_graph_viz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ use cranelift_entity::{entity_impl, PrimaryMap};
use dot2::label::Text;
use rustc_hash::{FxHashMap, FxHashSet};

use crate::{hir_def::ItemKind, HirDb};

use super::scope_graph::{EdgeKind, ScopeGraph, ScopeId};
use crate::{hir_def::ItemKind, HirDb};

type NodeId = usize;

Expand Down Expand Up @@ -113,7 +112,7 @@ impl<'db, 'a> dot2::Labeller<'a> for ScopeGraphFormatter<'db> {
}

ScopeId::Block(body, expr) => {
let idx = body.block_order(self.db)[expr];
let idx = body.iter_block(self.db)[expr];
format!(
r#" <font color="{block_color}">{{block{block_number}}}</font> "#,
block_color = "#383A42",
Expand Down
Loading

0 comments on commit e0585df

Please sign in to comment.