Skip to content
Merged
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
2 changes: 1 addition & 1 deletion crates/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ paste.workspace = true
salsa.workspace = true
serde-semver.workspace = true
smol_str.workspace = true
rust-embed = { version = "8.5.0", features = ["debug-embed"] }
rust-embed = "8.5.0"
toml = "0.8.8"
tracing.workspace = true
petgraph.workspace = true
Expand Down
17 changes: 7 additions & 10 deletions crates/common/src/ingot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,16 +201,13 @@ impl Workspace {
.directory()
.expect("Config URL should have a directory");

Some(Ingot::new(
db,
base_url.clone(),
None,
if base_url.scheme().contains("core") {
IngotKind::Core
} else {
IngotKind::Local
},
))
let kind = if base_url.scheme().contains("core") {
IngotKind::Core
} else {
IngotKind::Local
};

Some(Ingot::new(db, base_url.clone(), None, kind))
} else {
// Make a standalone ingot if no config is found
let base = location.directory().unwrap_or_else(|| location.clone());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---
source: crates/fe/tests/cli_output.rs
assertion_line: 77
expression: output
input_file: tests/fixtures/cli_output/single_files/parse_error.fe
---
Expand All @@ -19,6 +18,15 @@ error[1-0001]: `if` expression requires a body
6 │ }
│ ^ expected `{`

error[8-0000]: type mismatch
┌─ parse_error.fe:4:8
4 │ if x > { // Missing comparison value
│ ╭────────^
5 │ │ return true
6 │ │ }
│ ╰─────^ expected `bool`, but `{integer}` is given

error[8-0013]: returned type mismatch
┌─ parse_error.fe:5:9
Expand Down
13 changes: 7 additions & 6 deletions crates/hir-analysis/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,19 +514,18 @@ impl DiagnosticVoucher for PathResDiag<'_> {
}
}

Self::AmbiguousTrait { primary, method_name, traits } => {
Self::AmbiguousTrait { primary, method_name, trait_insts } => {
let method_name = method_name.data(db);
let mut sub_diagnostics = vec![SubDiagnostic {
style: LabelStyle::Primary,
message: format!("`{method_name}` is ambiguous"),
span: primary.resolve(db),
}];

for trait_ in traits {
let trait_name = trait_.name(db).unwrap().data(db);
for inst in trait_insts {
sub_diagnostics.push(SubDiagnostic {
style: LabelStyle::Secondary,
message: format!("candidate: `{trait_name}::{method_name}`"),
message: format!("candidate: `{}::{method_name}`", inst.pretty_print(db, false)),
span: primary.resolve(db),
});
}
Expand Down Expand Up @@ -1859,10 +1858,12 @@ impl DiagnosticVoucher for BodyDiag<'_> {
}];

for trait_ in traits {
let trait_name = trait_.name(db).unwrap().data(db);
sub_diagnostics.push(SubDiagnostic {
style: LabelStyle::Secondary,
message: format!("candidate: `{trait_name}::{method_name}`"),
message: format!(
"candidate: `{}::{method_name}`",
trait_.pretty_print(db, false)
),
span: primary.resolve(db),
});
}
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-analysis/src/name_resolution/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub enum PathResDiag<'db> {
AmbiguousTrait {
primary: DynLazySpan<'db>,
method_name: IdentId<'db>,
traits: ThinVec<Trait<'db>>,
trait_insts: ThinVec<TraitInstId<'db>>,
},
InvisibleAmbiguousTrait {
primary: DynLazySpan<'db>,
Expand Down
129 changes: 47 additions & 82 deletions crates/hir-analysis/src/name_resolution/method_selection.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use common::indexmap::{IndexMap, IndexSet};
use common::indexmap::IndexSet;
use hir::hir_def::{scope_graph::ScopeId, IdentId, Trait};
use itertools::Itertools;
use rustc_hash::FxHashSet;
use thin_vec::ThinVec;

use crate::{
name_resolution::{available_traits_in_scope, is_scope_visible_from},
ty::{
binder::Binder,
canonical::{Canonical, Canonicalized, Solution},
fold::TyFoldable,
func_def::FuncDef,
method_table::probe_method,
trait_def::{impls_for_ty, TraitDef, TraitInstId, TraitMethod},
Expand Down Expand Up @@ -56,12 +55,14 @@ pub(crate) fn select_method_candidate<'db>(
method_name: IdentId<'db>,
scope: ScopeId<'db>,
assumptions: PredicateListId<'db>,
trait_: Option<TraitDef<'db>>,
) -> Result<MethodCandidate<'db>, MethodSelectionError<'db>> {
if receiver.value.is_ty_var(db) {
return Err(MethodSelectionError::ReceiverTypeMustBeKnown);
}

let candidates = assemble_method_candidates(db, receiver, method_name, scope, assumptions);
let candidates =
assemble_method_candidates(db, receiver, method_name, scope, assumptions, trait_);

let selector = MethodSelector {
db,
Expand All @@ -80,13 +81,15 @@ fn assemble_method_candidates<'db>(
method_name: IdentId<'db>,
scope: ScopeId<'db>,
assumptions: PredicateListId<'db>,
trait_: Option<TraitDef<'db>>,
) -> AssembledCandidates<'db> {
CandidateAssembler {
db,
receiver_ty,
method_name,
scope,
assumptions,
trait_,
candidates: AssembledCandidates::default(),
}
.assemble()
Expand All @@ -102,12 +105,15 @@ struct CandidateAssembler<'db> {
scope: ScopeId<'db>,
/// The assumptions for the type bound in the current scope.
assumptions: PredicateListId<'db>,
trait_: Option<TraitDef<'db>>,
candidates: AssembledCandidates<'db>,
}

impl<'db> CandidateAssembler<'db> {
fn assemble(mut self) -> AssembledCandidates<'db> {
self.assemble_inherent_method_candidates();
if self.trait_.is_none() {
self.assemble_inherent_method_candidates();
}
self.assemble_trait_method_candidates();
self.candidates
}
Expand All @@ -125,45 +131,42 @@ impl<'db> CandidateAssembler<'db> {

fn assemble_trait_method_candidates(&mut self) {
let ingot = self.scope.ingot(self.db);
let mut table = UnificationTable::new(self.db);
let extracted_receiver_ty = self.receiver_ty.extract_identity(&mut table);

for &implementor in impls_for_ty(self.db, ingot, self.receiver_ty) {
let trait_def = implementor.skip_binder().trait_def(self.db);
self.insert_trait_method_cand(trait_def)
for &imp in impls_for_ty(self.db, ingot, self.receiver_ty) {
self.insert_trait_method_cand(imp.skip_binder().trait_(self.db));
}

let mut table = UnificationTable::new(self.db);
let extracted_receiver_ty = self.receiver_ty.extract_identity(&mut table);

for &pred in self.assumptions.list(self.db) {
let snapshot = table.snapshot();
let self_ty = pred.self_ty(self.db);
let self_ty = table.instantiate_to_term(self_ty);

if table.unify(extracted_receiver_ty, self_ty).is_ok() {
self.insert_trait_method_cand_with_inst(pred.def(self.db), pred);
self.insert_trait_method_cand(pred);
for super_trait in pred.def(self.db).super_traits(self.db) {
let super_trait = super_trait.instantiate(self.db, pred.args(self.db));
self.insert_trait_method_cand_with_inst(super_trait.def(self.db), super_trait);
self.insert_trait_method_cand(super_trait);
}
}

table.rollback_to(snapshot);
}
}

fn insert_trait_method_cand(&mut self, trait_def: TraitDef<'db>) {
if let Some(&trait_method) = trait_def.methods(self.db).get(&self.method_name) {
self.candidates.insert_trait(trait_def, trait_method);
}
fn allow_trait(&self, trait_def: TraitDef<'db>) -> bool {
self.trait_.map(|t| t == trait_def).unwrap_or(true)
}

fn insert_trait_method_cand_with_inst(
&mut self,
trait_def: TraitDef<'db>,
trait_inst: TraitInstId<'db>,
) {
fn insert_trait_method_cand(&mut self, inst: TraitInstId<'db>) {
let trait_def = inst.def(self.db);
if !self.allow_trait(trait_def) {
return;
}
if let Some(&trait_method) = trait_def.methods(self.db).get(&self.method_name) {
self.candidates
.insert_trait_with_inst(trait_def, trait_method, trait_inst);
self.candidates.traits.insert((inst, trait_method));
}
}
}
Expand Down Expand Up @@ -234,15 +237,15 @@ impl<'db> MethodSelector<'db> {
let traits = &self.candidates.traits;

if traits.len() == 1 {
let (def, method) = traits.iter().next().unwrap();
return Ok(self.find_inst(*def, *method));
let (inst, method) = traits.iter().next().unwrap();
return Ok(self.check_inst(*inst, *method));
}

let available_traits = self.available_traits();
let visible_traits: Vec<_> = traits
.iter()
.copied()
.filter(|cand| available_traits.contains(&cand.0))
.filter(|(inst, _method)| available_traits.contains(&inst.def(self.db)))
.collect();

match visible_traits.len() {
Expand All @@ -251,14 +254,17 @@ impl<'db> MethodSelector<'db> {
Err(MethodSelectionError::NotFound)
} else {
// Suggests trait imports.
let traits = traits.iter().map(|(def, _)| def.trait_(self.db)).collect();
let traits = traits
.iter()
.map(|(inst, _)| inst.def(self.db).trait_(self.db))
.collect();
Err(MethodSelectionError::InvisibleTraitMethod(traits))
}
}

1 => {
let (def, method) = visible_traits[0];
Ok(self.find_inst(def, method))
Ok(self.check_inst(def, method))
}

_ => Err(MethodSelectionError::AmbiguousTraitMethod(
Expand All @@ -275,39 +281,14 @@ impl<'db> MethodSelector<'db> {
/// checks if the goal is satisfiable given the current assumptions.
/// Depending on the result, it either returns a confirmed trait method
/// candidate or one that needs further confirmation.
///
/// # Arguments
///
/// * `def` - The trait definition.
/// * `method` - The trait method.
///
/// # Returns
///
/// A `Candidate` representing the found trait method instance.
fn find_inst(&self, def: TraitDef<'db>, method: TraitMethod<'db>) -> MethodCandidate<'db> {
fn check_inst(&self, inst: TraitInstId<'db>, method: TraitMethod<'db>) -> MethodCandidate<'db> {
let mut table = UnificationTable::new(self.db);
let receiver = self.receiver.extract_identity(&mut table);
let receiver = table.instantiate_to_term(receiver); // xxx remove?

// Check if we have a stored trait instance with associated type bindings
let cand = if let Some(&stored_inst) = self.candidates.trait_instances.get(&(def, method)) {
// Use the stored instance which includes associated type bindings
stored_inst
} else {
// Create a fresh instance without bindings
let inst_args = def
.params(self.db)
.iter()
.map(|ty| table.new_var_from_param(*ty))
.collect_vec();
TraitInstId::new(self.db, def, inst_args, IndexMap::new())
};

// Unify receiver and method self.
method.instantiate_with_inst(&mut table, receiver, cand);
// Seed the table with receiver's canonical variables so that subsequent
// canonicalization can safely probe them.
let _ = self.receiver.extract_identity(&mut table);

let cand = cand.fold_with(&mut table);
let canonical_cand = Canonicalized::new(self.db, cand);
let canonical_cand = Canonicalized::new(self.db, inst);
let inst = table.instantiate_with_fresh_vars(Binder::bind(inst));

match is_goal_satisfiable(
self.db,
Expand All @@ -318,13 +299,13 @@ impl<'db> MethodSelector<'db> {
GoalSatisfiability::Satisfied(solution) => {
// Map back the solution to the current context.
let solution = canonical_cand.extract_solution(&mut table, *solution);

// Unify candidate to solution.
table.unify(cand, solution).unwrap();
// Replace TyParams in the solved instance with fresh inference vars so
// downstream unification can bind them (e.g., T = u32).
let solution = table.instantiate_with_fresh_vars(Binder::bind(solution));

MethodCandidate::TraitMethod(TraitMethodCand::new(
self.receiver
.canonicalize_solution(self.db, &mut table, cand),
.canonicalize_solution(self.db, &mut table, solution),
method,
))
}
Expand All @@ -334,7 +315,7 @@ impl<'db> MethodSelector<'db> {
| GoalSatisfiability::UnSat(_) => {
MethodCandidate::NeedsConfirmation(TraitMethodCand::new(
self.receiver
.canonicalize_solution(self.db, &mut table, cand),
.canonicalize_solution(self.db, &mut table, inst),
method,
))
}
Expand Down Expand Up @@ -373,7 +354,7 @@ impl<'db> MethodSelector<'db> {
#[derive(Debug, Clone, PartialEq, Eq, Hash, salsa::Update)]
pub enum MethodSelectionError<'db> {
AmbiguousInherentMethod(ThinVec<FuncDef<'db>>),
AmbiguousTraitMethod(ThinVec<TraitDef<'db>>),
AmbiguousTraitMethod(ThinVec<TraitInstId<'db>>),
NotFound,
InvisibleInherentMethod(FuncDef<'db>),
InvisibleTraitMethod(ThinVec<Trait<'db>>),
Expand All @@ -383,27 +364,11 @@ pub enum MethodSelectionError<'db> {
#[derive(Default)]
struct AssembledCandidates<'db> {
inherent_methods: FxHashSet<FuncDef<'db>>,
traits: IndexSet<(TraitDef<'db>, TraitMethod<'db>)>,
// Store trait instances with their associated type bindings
trait_instances: IndexMap<(TraitDef<'db>, TraitMethod<'db>), TraitInstId<'db>>,
traits: IndexSet<(TraitInstId<'db>, TraitMethod<'db>)>,
}

impl<'db> AssembledCandidates<'db> {
fn insert_inherent_method(&mut self, method: FuncDef<'db>) {
self.inherent_methods.insert(method);
}

fn insert_trait(&mut self, def: TraitDef<'db>, method: TraitMethod<'db>) {
self.traits.insert((def, method));
}

fn insert_trait_with_inst(
&mut self,
def: TraitDef<'db>,
method: TraitMethod<'db>,
inst: TraitInstId<'db>,
) {
self.traits.insert((def, method));
self.trait_instances.insert((def, method), inst);
}
}
Loading