Skip to content

Commit cf5c5e6

Browse files
authored
Merge pull request #1119 from sbillig/ops-traits
add support for operator overloading via ops traits
2 parents 89a32d2 + bd96461 commit cf5c5e6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+3112
-759
lines changed

crates/common/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ paste.workspace = true
1515
salsa.workspace = true
1616
serde-semver.workspace = true
1717
smol_str.workspace = true
18-
rust-embed = { version = "8.5.0", features = ["debug-embed"] }
18+
rust-embed = "8.5.0"
1919
toml = "0.8.8"
2020
tracing.workspace = true
2121
petgraph.workspace = true

crates/common/src/ingot.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -201,16 +201,13 @@ impl Workspace {
201201
.directory()
202202
.expect("Config URL should have a directory");
203203

204-
Some(Ingot::new(
205-
db,
206-
base_url.clone(),
207-
None,
208-
if base_url.scheme().contains("core") {
209-
IngotKind::Core
210-
} else {
211-
IngotKind::Local
212-
},
213-
))
204+
let kind = if base_url.scheme().contains("core") {
205+
IngotKind::Core
206+
} else {
207+
IngotKind::Local
208+
};
209+
210+
Some(Ingot::new(db, base_url.clone(), None, kind))
214211
} else {
215212
// Make a standalone ingot if no config is found
216213
let base = location.directory().unwrap_or_else(|| location.clone());

crates/fe/tests/fixtures/cli_output/single_files/parse_error.snap

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
---
22
source: crates/fe/tests/cli_output.rs
3-
assertion_line: 77
43
expression: output
54
input_file: tests/fixtures/cli_output/single_files/parse_error.fe
65
---
@@ -19,6 +18,15 @@ error[1-0001]: `if` expression requires a body
1918
6 │ }
2019
^ expected `{`
2120
21+
error[8-0000]: type mismatch
22+
┌─ parse_error.fe:4:8
23+
24+
4 │ if x > { // Missing comparison value
25+
│ ╭────────^
26+
5 │ │ return true
27+
6 │ │ }
28+
│ ╰─────^ expected `bool`, but `{integer}` is given
29+
2230
error[8-0013]: returned type mismatch
2331
┌─ parse_error.fe:5:9
2432

crates/hir-analysis/src/diagnostics.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -514,19 +514,18 @@ impl DiagnosticVoucher for PathResDiag<'_> {
514514
}
515515
}
516516

517-
Self::AmbiguousTrait { primary, method_name, traits } => {
517+
Self::AmbiguousTrait { primary, method_name, trait_insts } => {
518518
let method_name = method_name.data(db);
519519
let mut sub_diagnostics = vec![SubDiagnostic {
520520
style: LabelStyle::Primary,
521521
message: format!("`{method_name}` is ambiguous"),
522522
span: primary.resolve(db),
523523
}];
524524

525-
for trait_ in traits {
526-
let trait_name = trait_.name(db).unwrap().data(db);
525+
for inst in trait_insts {
527526
sub_diagnostics.push(SubDiagnostic {
528527
style: LabelStyle::Secondary,
529-
message: format!("candidate: `{trait_name}::{method_name}`"),
528+
message: format!("candidate: `{}::{method_name}`", inst.pretty_print(db, false)),
530529
span: primary.resolve(db),
531530
});
532531
}
@@ -1859,10 +1858,12 @@ impl DiagnosticVoucher for BodyDiag<'_> {
18591858
}];
18601859

18611860
for trait_ in traits {
1862-
let trait_name = trait_.name(db).unwrap().data(db);
18631861
sub_diagnostics.push(SubDiagnostic {
18641862
style: LabelStyle::Secondary,
1865-
message: format!("candidate: `{trait_name}::{method_name}`"),
1863+
message: format!(
1864+
"candidate: `{}::{method_name}`",
1865+
trait_.pretty_print(db, false)
1866+
),
18661867
span: primary.resolve(db),
18671868
});
18681869
}

crates/hir-analysis/src/name_resolution/diagnostics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ pub enum PathResDiag<'db> {
8484
AmbiguousTrait {
8585
primary: DynLazySpan<'db>,
8686
method_name: IdentId<'db>,
87-
traits: ThinVec<Trait<'db>>,
87+
trait_insts: ThinVec<TraitInstId<'db>>,
8888
},
8989
InvisibleAmbiguousTrait {
9090
primary: DynLazySpan<'db>,

crates/hir-analysis/src/name_resolution/method_selection.rs

Lines changed: 47 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
use common::indexmap::{IndexMap, IndexSet};
1+
use common::indexmap::IndexSet;
22
use hir::hir_def::{scope_graph::ScopeId, IdentId, Trait};
3-
use itertools::Itertools;
43
use rustc_hash::FxHashSet;
54
use thin_vec::ThinVec;
65

76
use crate::{
87
name_resolution::{available_traits_in_scope, is_scope_visible_from},
98
ty::{
9+
binder::Binder,
1010
canonical::{Canonical, Canonicalized, Solution},
11-
fold::TyFoldable,
1211
func_def::FuncDef,
1312
method_table::probe_method,
1413
trait_def::{impls_for_ty, TraitDef, TraitInstId, TraitMethod},
@@ -56,12 +55,14 @@ pub(crate) fn select_method_candidate<'db>(
5655
method_name: IdentId<'db>,
5756
scope: ScopeId<'db>,
5857
assumptions: PredicateListId<'db>,
58+
trait_: Option<TraitDef<'db>>,
5959
) -> Result<MethodCandidate<'db>, MethodSelectionError<'db>> {
6060
if receiver.value.is_ty_var(db) {
6161
return Err(MethodSelectionError::ReceiverTypeMustBeKnown);
6262
}
6363

64-
let candidates = assemble_method_candidates(db, receiver, method_name, scope, assumptions);
64+
let candidates =
65+
assemble_method_candidates(db, receiver, method_name, scope, assumptions, trait_);
6566

6667
let selector = MethodSelector {
6768
db,
@@ -80,13 +81,15 @@ fn assemble_method_candidates<'db>(
8081
method_name: IdentId<'db>,
8182
scope: ScopeId<'db>,
8283
assumptions: PredicateListId<'db>,
84+
trait_: Option<TraitDef<'db>>,
8385
) -> AssembledCandidates<'db> {
8486
CandidateAssembler {
8587
db,
8688
receiver_ty,
8789
method_name,
8890
scope,
8991
assumptions,
92+
trait_,
9093
candidates: AssembledCandidates::default(),
9194
}
9295
.assemble()
@@ -102,12 +105,15 @@ struct CandidateAssembler<'db> {
102105
scope: ScopeId<'db>,
103106
/// The assumptions for the type bound in the current scope.
104107
assumptions: PredicateListId<'db>,
108+
trait_: Option<TraitDef<'db>>,
105109
candidates: AssembledCandidates<'db>,
106110
}
107111

108112
impl<'db> CandidateAssembler<'db> {
109113
fn assemble(mut self) -> AssembledCandidates<'db> {
110-
self.assemble_inherent_method_candidates();
114+
if self.trait_.is_none() {
115+
self.assemble_inherent_method_candidates();
116+
}
111117
self.assemble_trait_method_candidates();
112118
self.candidates
113119
}
@@ -125,45 +131,42 @@ impl<'db> CandidateAssembler<'db> {
125131

126132
fn assemble_trait_method_candidates(&mut self) {
127133
let ingot = self.scope.ingot(self.db);
128-
let mut table = UnificationTable::new(self.db);
129-
let extracted_receiver_ty = self.receiver_ty.extract_identity(&mut table);
130134

131-
for &implementor in impls_for_ty(self.db, ingot, self.receiver_ty) {
132-
let trait_def = implementor.skip_binder().trait_def(self.db);
133-
self.insert_trait_method_cand(trait_def)
135+
for &imp in impls_for_ty(self.db, ingot, self.receiver_ty) {
136+
self.insert_trait_method_cand(imp.skip_binder().trait_(self.db));
134137
}
135138

139+
let mut table = UnificationTable::new(self.db);
140+
let extracted_receiver_ty = self.receiver_ty.extract_identity(&mut table);
141+
136142
for &pred in self.assumptions.list(self.db) {
137143
let snapshot = table.snapshot();
138144
let self_ty = pred.self_ty(self.db);
139145
let self_ty = table.instantiate_to_term(self_ty);
140146

141147
if table.unify(extracted_receiver_ty, self_ty).is_ok() {
142-
self.insert_trait_method_cand_with_inst(pred.def(self.db), pred);
148+
self.insert_trait_method_cand(pred);
143149
for super_trait in pred.def(self.db).super_traits(self.db) {
144150
let super_trait = super_trait.instantiate(self.db, pred.args(self.db));
145-
self.insert_trait_method_cand_with_inst(super_trait.def(self.db), super_trait);
151+
self.insert_trait_method_cand(super_trait);
146152
}
147153
}
148154

149155
table.rollback_to(snapshot);
150156
}
151157
}
152158

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

159-
fn insert_trait_method_cand_with_inst(
160-
&mut self,
161-
trait_def: TraitDef<'db>,
162-
trait_inst: TraitInstId<'db>,
163-
) {
163+
fn insert_trait_method_cand(&mut self, inst: TraitInstId<'db>) {
164+
let trait_def = inst.def(self.db);
165+
if !self.allow_trait(trait_def) {
166+
return;
167+
}
164168
if let Some(&trait_method) = trait_def.methods(self.db).get(&self.method_name) {
165-
self.candidates
166-
.insert_trait_with_inst(trait_def, trait_method, trait_inst);
169+
self.candidates.traits.insert((inst, trait_method));
167170
}
168171
}
169172
}
@@ -234,15 +237,15 @@ impl<'db> MethodSelector<'db> {
234237
let traits = &self.candidates.traits;
235238

236239
if traits.len() == 1 {
237-
let (def, method) = traits.iter().next().unwrap();
238-
return Ok(self.find_inst(*def, *method));
240+
let (inst, method) = traits.iter().next().unwrap();
241+
return Ok(self.check_inst(*inst, *method));
239242
}
240243

241244
let available_traits = self.available_traits();
242245
let visible_traits: Vec<_> = traits
243246
.iter()
244247
.copied()
245-
.filter(|cand| available_traits.contains(&cand.0))
248+
.filter(|(inst, _method)| available_traits.contains(&inst.def(self.db)))
246249
.collect();
247250

248251
match visible_traits.len() {
@@ -251,14 +254,17 @@ impl<'db> MethodSelector<'db> {
251254
Err(MethodSelectionError::NotFound)
252255
} else {
253256
// Suggests trait imports.
254-
let traits = traits.iter().map(|(def, _)| def.trait_(self.db)).collect();
257+
let traits = traits
258+
.iter()
259+
.map(|(inst, _)| inst.def(self.db).trait_(self.db))
260+
.collect();
255261
Err(MethodSelectionError::InvisibleTraitMethod(traits))
256262
}
257263
}
258264

259265
1 => {
260266
let (def, method) = visible_traits[0];
261-
Ok(self.find_inst(def, method))
267+
Ok(self.check_inst(def, method))
262268
}
263269

264270
_ => Err(MethodSelectionError::AmbiguousTraitMethod(
@@ -275,39 +281,14 @@ impl<'db> MethodSelector<'db> {
275281
/// checks if the goal is satisfiable given the current assumptions.
276282
/// Depending on the result, it either returns a confirmed trait method
277283
/// candidate or one that needs further confirmation.
278-
///
279-
/// # Arguments
280-
///
281-
/// * `def` - The trait definition.
282-
/// * `method` - The trait method.
283-
///
284-
/// # Returns
285-
///
286-
/// A `Candidate` representing the found trait method instance.
287-
fn find_inst(&self, def: TraitDef<'db>, method: TraitMethod<'db>) -> MethodCandidate<'db> {
284+
fn check_inst(&self, inst: TraitInstId<'db>, method: TraitMethod<'db>) -> MethodCandidate<'db> {
288285
let mut table = UnificationTable::new(self.db);
289-
let receiver = self.receiver.extract_identity(&mut table);
290-
let receiver = table.instantiate_to_term(receiver); // xxx remove?
291-
292-
// Check if we have a stored trait instance with associated type bindings
293-
let cand = if let Some(&stored_inst) = self.candidates.trait_instances.get(&(def, method)) {
294-
// Use the stored instance which includes associated type bindings
295-
stored_inst
296-
} else {
297-
// Create a fresh instance without bindings
298-
let inst_args = def
299-
.params(self.db)
300-
.iter()
301-
.map(|ty| table.new_var_from_param(*ty))
302-
.collect_vec();
303-
TraitInstId::new(self.db, def, inst_args, IndexMap::new())
304-
};
305-
306-
// Unify receiver and method self.
307-
method.instantiate_with_inst(&mut table, receiver, cand);
286+
// Seed the table with receiver's canonical variables so that subsequent
287+
// canonicalization can safely probe them.
288+
let _ = self.receiver.extract_identity(&mut table);
308289

309-
let cand = cand.fold_with(&mut table);
310-
let canonical_cand = Canonicalized::new(self.db, cand);
290+
let canonical_cand = Canonicalized::new(self.db, inst);
291+
let inst = table.instantiate_with_fresh_vars(Binder::bind(inst));
311292

312293
match is_goal_satisfiable(
313294
self.db,
@@ -318,13 +299,13 @@ impl<'db> MethodSelector<'db> {
318299
GoalSatisfiability::Satisfied(solution) => {
319300
// Map back the solution to the current context.
320301
let solution = canonical_cand.extract_solution(&mut table, *solution);
321-
322-
// Unify candidate to solution.
323-
table.unify(cand, solution).unwrap();
302+
// Replace TyParams in the solved instance with fresh inference vars so
303+
// downstream unification can bind them (e.g., T = u32).
304+
let solution = table.instantiate_with_fresh_vars(Binder::bind(solution));
324305

325306
MethodCandidate::TraitMethod(TraitMethodCand::new(
326307
self.receiver
327-
.canonicalize_solution(self.db, &mut table, cand),
308+
.canonicalize_solution(self.db, &mut table, solution),
328309
method,
329310
))
330311
}
@@ -334,7 +315,7 @@ impl<'db> MethodSelector<'db> {
334315
| GoalSatisfiability::UnSat(_) => {
335316
MethodCandidate::NeedsConfirmation(TraitMethodCand::new(
336317
self.receiver
337-
.canonicalize_solution(self.db, &mut table, cand),
318+
.canonicalize_solution(self.db, &mut table, inst),
338319
method,
339320
))
340321
}
@@ -373,7 +354,7 @@ impl<'db> MethodSelector<'db> {
373354
#[derive(Debug, Clone, PartialEq, Eq, Hash, salsa::Update)]
374355
pub enum MethodSelectionError<'db> {
375356
AmbiguousInherentMethod(ThinVec<FuncDef<'db>>),
376-
AmbiguousTraitMethod(ThinVec<TraitDef<'db>>),
357+
AmbiguousTraitMethod(ThinVec<TraitInstId<'db>>),
377358
NotFound,
378359
InvisibleInherentMethod(FuncDef<'db>),
379360
InvisibleTraitMethod(ThinVec<Trait<'db>>),
@@ -383,27 +364,11 @@ pub enum MethodSelectionError<'db> {
383364
#[derive(Default)]
384365
struct AssembledCandidates<'db> {
385366
inherent_methods: FxHashSet<FuncDef<'db>>,
386-
traits: IndexSet<(TraitDef<'db>, TraitMethod<'db>)>,
387-
// Store trait instances with their associated type bindings
388-
trait_instances: IndexMap<(TraitDef<'db>, TraitMethod<'db>), TraitInstId<'db>>,
367+
traits: IndexSet<(TraitInstId<'db>, TraitMethod<'db>)>,
389368
}
390369

391370
impl<'db> AssembledCandidates<'db> {
392371
fn insert_inherent_method(&mut self, method: FuncDef<'db>) {
393372
self.inherent_methods.insert(method);
394373
}
395-
396-
fn insert_trait(&mut self, def: TraitDef<'db>, method: TraitMethod<'db>) {
397-
self.traits.insert((def, method));
398-
}
399-
400-
fn insert_trait_with_inst(
401-
&mut self,
402-
def: TraitDef<'db>,
403-
method: TraitMethod<'db>,
404-
inst: TraitInstId<'db>,
405-
) {
406-
self.traits.insert((def, method));
407-
self.trait_instances.insert((def, method), inst);
408-
}
409374
}

0 commit comments

Comments
 (0)