Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Matching anomaly #1010

Draft
wants to merge 2 commits into
base: fe-v2
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions crates/codegen/src/yul/isel/inst_order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,13 +464,13 @@ impl<'a> InstSerializer<'a> {
///
/// The scoring function `F` is defined as follows:
/// 1. The initial score of each candidate('cand_bb`) is number of
/// predecessors of the candidate.
/// predecessors of the candidate.
///
/// 2. Find the `top_cand` of each `cand_bb`. `top_cand` can be found by
/// [`Self::try_find_top_cand`] method, see the method for details.
/// [`Self::try_find_top_cand`] method, see the method for details.
///
/// 3. If `top_cand` is found, then add the `cand_bb` score to the
/// `top_cand` score, then set 0 to the `cand_bb` score.
/// `top_cand` score, then set 0 to the `cand_bb` score.
///
/// After the scoring, the candidates with the highest score will be
/// selected.
Expand Down Expand Up @@ -516,10 +516,10 @@ impl<'a> InstSerializer<'a> {
/// A `top_cand` can be found by the following rules:
///
/// 1. Find the block which is contained in DF of `cand_bb` and in
/// `cands_with_score`.
/// `cands_with_score`.
///
/// 2. If a block is found in 1., and the score of the block is positive,
/// then the block is `top_cand`.
/// then the block is `top_cand`.
///
/// 2'. If a block is found in 1., and the score of the block is 0, then the
/// `top_cand` of the block is `top_cand` of `cand_bb`.
Expand Down
1 change: 1 addition & 0 deletions crates/hir-analysis/src/ty/def_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ impl<'db> DefAnalyzer<'db> {
/// This method verifies if
/// 1. the given `ty` has `*` kind.
/// 2. the given `ty` is not const type
///
/// TODO: This method is a stop-gap implementation until we design a true
/// const type system.
fn verify_term_type_kind(&mut self, ty: HirTyId<'db>, span: DynLazySpan<'db>) -> bool {
Expand Down
89 changes: 59 additions & 30 deletions crates/hir-analysis/src/ty/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,11 @@ pub enum BodyDiag<'db> {
TypeMismatch(DynLazySpan<'db>, String, String),
InfiniteOccurrence(DynLazySpan<'db>),

DuplicatedBinding {
primary: DynLazySpan<'db>,
conflicat_with: DynLazySpan<'db>,
name: IdentId<'db>,
},
DuplicatedRestPat(DynLazySpan<'db>),

InvalidPathDomainInPat {
Expand Down Expand Up @@ -878,43 +883,47 @@ impl<'db> BodyDiag<'db> {
match self {
Self::TypeMismatch(..) => 0,
Self::InfiniteOccurrence(..) => 1,
Self::DuplicatedRestPat(..) => 2,
Self::InvalidPathDomainInPat { .. } => 3,
Self::UnitVariantExpected { .. } => 4,
Self::TupleVariantExpected { .. } => 5,
Self::RecordExpected { .. } => 6,
Self::MismatchedFieldCount { .. } => 7,
Self::DuplicatedRecordFieldBind { .. } => 8,
Self::RecordFieldNotFound { .. } => 9,
Self::ExplicitLabelExpectedInRecord { .. } => 10,
Self::MissingRecordFields { .. } => 11,
Self::UndefinedVariable(..) => 12,
Self::ReturnedTypeMismatch { .. } => 13,
Self::TypeMustBeKnown(..) => 14,
Self::AccessedFieldNotFound { .. } => 15,
Self::OpsTraitNotImplemented { .. } => 16,
Self::NonAssignableExpr(..) => 17,
Self::ImmutableAssignment { .. } => 18,
Self::LoopControlOutsideOfLoop { .. } => 19,
Self::TraitNotImplemented { .. } => 20,
Self::NotCallable(..) => 21,
Self::CallGenericArgNumMismatch { .. } => 22,
Self::CallArgNumMismatch { .. } => 23,
Self::CallArgLabelMismatch { .. } => 24,
Self::AmbiguousInherentMethodCall { .. } => 25,
Self::AmbiguousTrait { .. } => 26,
Self::AmbiguousTraitInst { .. } => 27,
Self::InvisibleAmbiguousTrait { .. } => 28,
Self::MethodNotFound { .. } => 29,
Self::NotValue { .. } => 30,
Self::TypeAnnotationNeeded { .. } => 31,
Self::DuplicatedBinding { .. } => 2,
Self::DuplicatedRestPat(..) => 3,
Self::InvalidPathDomainInPat { .. } => 4,
Self::UnitVariantExpected { .. } => 5,
Self::TupleVariantExpected { .. } => 6,
Self::RecordExpected { .. } => 7,
Self::MismatchedFieldCount { .. } => 8,
Self::DuplicatedRecordFieldBind { .. } => 9,
Self::RecordFieldNotFound { .. } => 10,
Self::ExplicitLabelExpectedInRecord { .. } => 11,
Self::MissingRecordFields { .. } => 12,
Self::UndefinedVariable(..) => 13,
Self::ReturnedTypeMismatch { .. } => 14,
Self::TypeMustBeKnown(..) => 15,
Self::AccessedFieldNotFound { .. } => 16,
Self::OpsTraitNotImplemented { .. } => 17,
Self::NonAssignableExpr(..) => 18,
Self::ImmutableAssignment { .. } => 19,
Self::LoopControlOutsideOfLoop { .. } => 20,
Self::TraitNotImplemented { .. } => 21,
Self::NotCallable(..) => 22,
Self::CallGenericArgNumMismatch { .. } => 23,
Self::CallArgNumMismatch { .. } => 24,
Self::CallArgLabelMismatch { .. } => 25,
Self::AmbiguousInherentMethodCall { .. } => 26,
Self::AmbiguousTrait { .. } => 27,
Self::AmbiguousTraitInst { .. } => 28,
Self::InvisibleAmbiguousTrait { .. } => 29,
Self::MethodNotFound { .. } => 30,
Self::NotValue { .. } => 31,
Self::TypeAnnotationNeeded { .. } => 32,
}
}

fn message(&self, db: &dyn HirDb) -> String {
match self {
Self::TypeMismatch(_, _, _) => "type mismatch".to_string(),
Self::InfiniteOccurrence(_) => "infinite sized type found".to_string(),
Self::DuplicatedBinding { name, .. } => {
format!("duplicated bidning `{}` in the same pattern", name.data(db))
}
Self::DuplicatedRestPat(_) => "duplicated `..` found".to_string(),
Self::InvalidPathDomainInPat { .. } => "invalid item is given here".to_string(),
Self::UnitVariantExpected { .. } => "expected unit variant".to_string(),
Expand Down Expand Up @@ -990,6 +999,26 @@ impl<'db> BodyDiag<'db> {
span.resolve(db),
)],

Self::DuplicatedBinding {
primary,
conflicat_with,
name,
} => {
let name = name.data(db.as_hir_db());
vec![
SubDiagnostic::new(
LabelStyle::Primary,
format!("`{name}` is already defined in the same pattern",),
primary.resolve(db),
),
SubDiagnostic::new(
LabelStyle::Secondary,
format!("`{name}` is defined here"),
conflicat_with.resolve(db),
),
]
}

Self::DuplicatedRestPat(span) => vec![SubDiagnostic::new(
LabelStyle::Primary,
"`..` can be used only once".to_string(),
Expand Down
1 change: 1 addition & 0 deletions crates/hir-analysis/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod diagnostics;
pub mod fold;
pub mod func_def;
pub mod method_table;
pub mod pattern_analysis;
pub mod trait_def;
pub mod trait_lower;
pub mod trait_resolution;
Expand Down
48 changes: 48 additions & 0 deletions crates/hir-analysis/src/ty/pattern_analysis/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use hir::hir_def::{IntegerId, PatId, StringId};

use super::{ty_check::TypedBody, ty_def::TyId};
use crate::HirAnalysisDb;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SimplifiedPat<'db> {
ctor: PatCtor<'db>,
args: Vec<Self>,
hir_pat: PatId,
ty: TyId<'db>,
}

impl<'db> SimplifiedPat<'db> {
pub fn new(
ctor: PatCtor<'db>,
args: Vec<SimplifiedPat<'db>>,
hir_pat: PatId,
ty: TyId<'db>,
) -> Self {
Self {
ctor,
args,
hir_pat,
ty,
}
}

pub fn simplify(db: &'db dyn HirAnalysisDb, pat: PatId, body: TypedBody) -> Self {
let ty = body.pat_ty(db, pat);
assert!(!ty.has_invalid(db));

todo!()
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PatCtor<'db> {
Or,
WildCard,
Tuple,
Struct,
Variant(u32),
// FIXME: Extend this to `IntRange` when we add range pattern.
Int(IntegerId<'db>),
String(StringId<'db>),
Bool(bool),
}
66 changes: 61 additions & 5 deletions crates/hir-analysis/src/ty/ty_check/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,17 +177,38 @@ impl<'db> TyCheckEnv<'db> {
self.pat_ty.insert(pat, ty);
}

/// Register a pending binding which will be added when `flush_pending_vars`
/// is called.
/// Registers a new pending binding.
///
/// This function adds a binding to the list of pending variables. If a
/// binding with the same name already exists, it returns the existing
/// binding. Otherwise, it returns `None`.
///
/// To flush pending bindings to the designated scope, call
/// [`flush_pending_bindings`] in the scope.
///
/// # Arguments
///
/// * `name` - The identifier of the variable.
/// * `binding` - The local binding to be registered.
///
/// # Returns
///
/// * `Some(LocalBinding)` if a binding with the same name already exists.
/// * `None` if the binding was successfully registered.
pub(super) fn register_pending_binding(
&mut self,
name: IdentId<'db>,
binding: LocalBinding<'db>,
) {
self.pending_vars.insert(name, binding);
) -> Option<LocalBinding<'db>> {
self.pending_vars.insert(name, binding)
}

/// Flush pending bindings to the current scope environment.
/// Flushes all pending variable bindings into the current variable
/// environment.
///
/// This function moves all pending bindings from the `pending_vars` map
/// into the latest `BlockEnv` in `var_env`. After this operation, the
/// `pending_vars` map will be empty.
pub(super) fn flush_pending_bindings(&mut self) {
let var_env = self.var_env.last_mut().unwrap();
for (name, binding) in self.pending_vars.drain() {
Expand All @@ -199,6 +220,25 @@ impl<'db> TyCheckEnv<'db> {
self.pending_confirmations.push((inst, span))
}

/// Completes the type checking environment by finalizing pending trait
/// confirmations, folding types with the unification table, and collecting
/// diagnostics.
///
/// # Arguments
///
/// * `table` - A mutable reference to the unification table used for type
/// unification.
///
/// # Returns
///
/// * A tuple containing the `TypedBody` and a vector of `FuncBodyDiag`.
///
/// The `TypedBody` includes the body of the function, pattern types,
/// expression types, and callables, all of which have been folded with
/// the unification table.
///
/// The vector of `FuncBodyDiag` contains diagnostics related to function
/// bodies, such as ambiguous trait instances.
pub(super) fn finish(
mut self,
table: &mut UnificationTable<'db>,
Expand Down Expand Up @@ -249,6 +289,22 @@ impl<'db> TyCheckEnv<'db> {
&self.var_env[idx]
}

/// Performs pending trait confirmations and collects diagnostics.
///
/// This function attempts to satisfy all pending trait confirmations by
/// iteratively probing and unifying trait instances until a fixed point
/// is reached. If any trait instance remains ambiguous, a diagnostic is
/// generated and added to the diagnostics vector.
///
/// # Arguments
///
/// * `prober` - A mutable reference to the [`Prober`] used for type
/// unification and probing.
///
/// # Returns
///
/// * A vector of `FuncBodyDiag` containing diagnostics related to ambiguous
/// trait instances.
fn perform_pending_confirmation(
&self,
prober: &mut Prober<'db, '_>,
Expand Down
40 changes: 35 additions & 5 deletions crates/hir-analysis/src/ty/ty_check/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl<'db> TyChecker<'db> {
(base, args) if base.is_tuple(self.db) => Some(args.len()),
_ => None,
};
let (actual, rest_range) = self.unpack_rest_pat(pat_tup, expected_len);
let (actual, rest_range) = self.unpack_tuple_pat(pat_tup, expected_len);
let actual = TyId::tuple_with_elems(self.db, &actual);

let unified = self.unify_ty(pat, actual, expected);
Expand Down Expand Up @@ -149,9 +149,19 @@ impl<'db> TyChecker<'db> {
}
}

ResolvedPathInBody::Binding(ident, _) | ResolvedPathInBody::NewBinding(ident) => {
ResolvedPathInBody::Binding(name, _) | ResolvedPathInBody::NewBinding(name) => {
let binding = LocalBinding::local(pat, *is_mut);
self.env.register_pending_binding(ident, binding);
if let Some(LocalBinding::Local {
pat: conflict_with, ..
}) = self.env.register_pending_binding(name, binding)
{
let diag = BodyDiag::DuplicatedBinding {
primary: span.into(),
conflicat_with: conflict_with.lazy_span(self.body()).into(),
name,
};
self.push_diag(diag);
}
self.fresh_ty()
}

Expand Down Expand Up @@ -233,7 +243,7 @@ impl<'db> TyChecker<'db> {
let pat_ty = variant.ty(self.db);
let expected_len = expected_elems.len(self.db.as_hir_db());

let (actual_elems, rest_range) = self.unpack_rest_pat(elems, Some(expected_len));
let (actual_elems, rest_range) = self.unpack_tuple_pat(elems, Some(expected_len));
if actual_elems.len() != expected_len {
let diag = BodyDiag::MismatchedFieldCount {
primary: pat.lazy_span(self.body()).into(),
Expand Down Expand Up @@ -393,7 +403,27 @@ impl<'db> TyChecker<'db> {
}
}

fn unpack_rest_pat(
/// Unpacks a tuple pattern, identifying the location of any rest
/// patterns and generating the appropriate types for each element in
/// the tuple.
///
/// # Parameters
/// - `pat_tup`: A slice of pattern representing the elements of the tuple
/// pattern.
/// - `expected_len`: An optional expected length for the tuple.
///
/// # Returns
/// A tuple containing:
/// - A vector of type for each element in the tuple.
/// - A range indicating the position of the rest pattern within the tuple,
/// if present.
///
/// # Notes
/// - If there are multiple rest patterns, a diagnostic message is
/// generated.
/// - If the minimum length of the tuple pattern exceeds the expected
/// length, a default range is returned.
fn unpack_tuple_pat(
&mut self,
pat_tup: &[PatId],
expected_len: Option<usize>,
Expand Down
4 changes: 1 addition & 3 deletions crates/uitest/fixtures/ty/trait_bound/coinductive.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ source: crates/uitest/tests/ty.rs
expression: diags
input_file: crates/uitest/fixtures/ty/trait_bound/coinductive.fe
---
error[8-0031]: type annotation is needed
error[8-0032]: type annotation is needed
┌─ coinductive.fe:35:9
35 │ let v = Vec::new()
│ ^
│ │
│ type annotation is needed
│ consider giving `: Vec<_>` here


Loading
Loading