Skip to content

Commit 15666ae

Browse files
committed
Implement new operator dispatch semantics.
Key points are: 1. `a + b` maps directly to `Add<A,B>`, where `A` and `B` are the types of `a` and `b`. 2. Indexing and slicing autoderefs consistently.
1 parent b8768cd commit 15666ae

15 files changed

+813
-222
lines changed

src/librustc/middle/ty.rs

+15
Original file line numberDiff line numberDiff line change
@@ -5576,3 +5576,18 @@ pub fn with_freevars<T>(tcx: &ty::ctxt, fid: ast::NodeId, f: |&[Freevar]| -> T)
55765576
Some(d) => f(d.as_slice())
55775577
}
55785578
}
5579+
5580+
impl AutoAdjustment {
5581+
pub fn is_identity(&self) -> bool {
5582+
match *self {
5583+
AdjustAddEnv(..) => false,
5584+
AdjustDerefRef(ref r) => r.is_identity(),
5585+
}
5586+
}
5587+
}
5588+
5589+
impl AutoDerefRef {
5590+
pub fn is_identity(&self) -> bool {
5591+
self.autoderefs == 0 && self.autoref.is_none()
5592+
}
5593+
}

src/librustc/middle/typeck/check/method.rs

+228-54
Original file line numberDiff line numberDiff line change
@@ -173,46 +173,170 @@ pub fn lookup<'a, 'tcx>(
173173

174174
pub fn lookup_in_trait<'a, 'tcx>(
175175
fcx: &'a FnCtxt<'a, 'tcx>,
176-
177-
// In a call `a.b::<X, Y, ...>(...)`:
178-
span: Span, // The expression `a.b(...)`'s span.
179-
self_expr: Option<&'a ast::Expr>, // The expression `a`, if available.
180-
m_name: ast::Name, // The name `b`.
181-
trait_did: DefId, // The trait to limit the lookup to.
182-
self_ty: ty::t, // The type of `a`.
183-
supplied_tps: &'a [ty::t]) // The list of types X, Y, ... .
176+
span: Span,
177+
self_expr: Option<&'a ast::Expr>,
178+
m_name: ast::Name,
179+
trait_def_id: DefId,
180+
self_ty: ty::t,
181+
opt_input_types: Option<Vec<ty::t>>)
184182
-> Option<MethodCallee>
185183
{
186-
let mut lcx = LookupContext {
187-
fcx: fcx,
188-
span: span,
189-
self_expr: self_expr,
190-
m_name: m_name,
191-
supplied_tps: supplied_tps,
192-
impl_dups: HashSet::new(),
193-
inherent_candidates: Vec::new(),
194-
extension_candidates: Vec::new(),
195-
static_candidates: Vec::new(),
196-
deref_args: check::DoDerefArgs,
197-
check_traits: CheckTraitsOnly,
198-
autoderef_receiver: DontAutoderefReceiver,
199-
};
184+
lookup_in_trait_adjusted(fcx, span, self_expr, m_name, trait_def_id,
185+
ty::AutoDerefRef { autoderefs: 0, autoref: None },
186+
self_ty, opt_input_types)
187+
}
200188

201-
debug!("method lookup_in_trait(self_ty={}, self_expr={}, m_name={}, trait_did={})",
189+
pub fn lookup_in_trait_adjusted<'a, 'tcx>(
190+
fcx: &'a FnCtxt<'a, 'tcx>,
191+
span: Span,
192+
self_expr: Option<&'a ast::Expr>,
193+
m_name: ast::Name,
194+
trait_def_id: DefId,
195+
autoderefref: ty::AutoDerefRef,
196+
self_ty: ty::t,
197+
opt_input_types: Option<Vec<ty::t>>)
198+
-> Option<MethodCallee>
199+
{
200+
debug!("method lookup_in_trait(self_ty={}, self_expr={}, m_name={}, trait_def_id={})",
202201
self_ty.repr(fcx.tcx()),
203202
self_expr.repr(fcx.tcx()),
204203
m_name.repr(fcx.tcx()),
205-
trait_did.repr(fcx.tcx()));
204+
trait_def_id.repr(fcx.tcx()));
205+
206+
let trait_def = ty::lookup_trait_def(fcx.tcx(), trait_def_id);
207+
208+
let expected_number_of_input_types = trait_def.generics.types.len(subst::TypeSpace);
209+
let input_types = match opt_input_types {
210+
Some(input_types) => {
211+
assert_eq!(expected_number_of_input_types, input_types.len());
212+
input_types
213+
}
214+
215+
None => {
216+
fcx.inh.infcx.next_ty_vars(expected_number_of_input_types)
217+
}
218+
};
219+
220+
assert_eq!(trait_def.generics.types.len(subst::FnSpace), 0);
221+
assert!(trait_def.generics.regions.is_empty());
206222

207-
lcx.push_bound_candidates(self_ty, Some(trait_did));
208-
lcx.push_extension_candidate(trait_did);
223+
// Construct a trait-reference `self_ty : Trait<input_tys>`
224+
let substs = subst::Substs::new_trait(input_types, Vec::new(), self_ty);
225+
let trait_ref = Rc::new(ty::TraitRef::new(trait_def_id, substs));
209226

210-
// when doing a trait search, ambiguity can't really happen except
211-
// as part of the trait-lookup in general
212-
match lcx.search(self_ty) {
213-
Ok(callee) => Some(callee),
214-
Err(_) => None
227+
// Construct an obligation
228+
let obligation = traits::Obligation::misc(span, trait_ref.clone());
229+
230+
// Now we want to know if this can be matched
231+
let mut selcx = traits::SelectionContext::new(fcx.infcx(),
232+
&fcx.inh.param_env,
233+
fcx);
234+
if !selcx.evaluate_obligation_intracrate(&obligation) {
235+
debug!("--> Cannot match obligation");
236+
return None; // Cannot be matched, no such method resolution is possible.
215237
}
238+
239+
// Trait must have a method named `m_name` and it should not have
240+
// type parameters or early-bound regions.
241+
let tcx = fcx.tcx();
242+
let (method_num, method_ty) = trait_method(tcx, trait_def_id, m_name).unwrap();
243+
assert_eq!(method_ty.generics.types.len(subst::FnSpace), 0);
244+
assert_eq!(method_ty.generics.regions.len(subst::FnSpace), 0);
245+
246+
// Substitute the trait parameters into the method type and
247+
// instantiate late-bound regions to get the actual method type.
248+
let ref bare_fn_ty = method_ty.fty;
249+
let fn_sig = bare_fn_ty.sig.subst(tcx, &trait_ref.substs);
250+
let fn_sig = replace_late_bound_regions_with_fresh_var(fcx.infcx(), span,
251+
fn_sig.binder_id, &fn_sig);
252+
let transformed_self_ty = fn_sig.inputs[0];
253+
let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {
254+
sig: fn_sig,
255+
fn_style: bare_fn_ty.fn_style,
256+
abi: bare_fn_ty.abi.clone(),
257+
});
258+
259+
debug!("matched method fty={} obligation={}",
260+
fty.repr(fcx.tcx()),
261+
obligation.repr(fcx.tcx()));
262+
263+
// Register obligations for the parameters. This will include the
264+
// `Self` parameter, which in turn has a bound of the main trait,
265+
// so this also effectively registers `obligation` as well. (We
266+
// used to register `obligation` explicitly, but that resulted in
267+
// double error messages being reported.)
268+
fcx.add_obligations_for_parameters(
269+
traits::ObligationCause::misc(span),
270+
&trait_ref.substs,
271+
&method_ty.generics);
272+
273+
// Insert any adjustments needed (always an autoref of some mutability).
274+
match self_expr {
275+
None => { }
276+
277+
Some(self_expr) => {
278+
debug!("inserting adjustment if needed (self-id = {}, \
279+
base adjustment = {}, explicit self = {})",
280+
self_expr.id, autoderefref, method_ty.explicit_self);
281+
282+
match method_ty.explicit_self {
283+
ty::ByValueExplicitSelfCategory => {
284+
// Trait method is fn(self), no transformation needed.
285+
if !autoderefref.is_identity() {
286+
fcx.write_adjustment(
287+
self_expr.id,
288+
span,
289+
ty::AdjustDerefRef(autoderefref));
290+
}
291+
}
292+
293+
ty::ByReferenceExplicitSelfCategory(..) => {
294+
// Trait method is fn(&self) or fn(&mut self), need an
295+
// autoref. Pull the region etc out of the type of first argument.
296+
match ty::get(transformed_self_ty).sty {
297+
ty::ty_rptr(region, ty::mt { mutbl, ty: _ }) => {
298+
let ty::AutoDerefRef { autoderefs, autoref } = autoderefref;
299+
let autoref = autoref.map(|r| box r);
300+
fcx.write_adjustment(
301+
self_expr.id,
302+
span,
303+
ty::AdjustDerefRef(ty::AutoDerefRef {
304+
autoderefs: autoderefs,
305+
autoref: Some(ty::AutoPtr(region, mutbl, autoref))
306+
}));
307+
}
308+
309+
_ => {
310+
fcx.tcx().sess.span_bug(
311+
span,
312+
format!(
313+
"trait method is &self but first arg is: {}",
314+
transformed_self_ty.repr(fcx.tcx())).as_slice());
315+
}
316+
}
317+
}
318+
319+
_ => {
320+
fcx.tcx().sess.span_bug(
321+
span,
322+
format!(
323+
"unexpected explicit self type in operator method: {}",
324+
method_ty.explicit_self).as_slice());
325+
}
326+
}
327+
}
328+
}
329+
330+
let callee = MethodCallee {
331+
origin: MethodTypeParam(MethodParam{trait_ref: trait_ref.clone(),
332+
method_num: method_num}),
333+
ty: fty,
334+
substs: trait_ref.substs.clone()
335+
};
336+
337+
debug!("callee = {}", callee.repr(fcx.tcx()));
338+
339+
Some(callee)
216340
}
217341

218342
pub fn report_error(fcx: &FnCtxt,
@@ -1446,9 +1570,8 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
14461570
}
14471571
}
14481572

1449-
fn fixup_derefs_on_method_receiver_if_necessary(
1450-
&self,
1451-
method_callee: &MethodCallee) {
1573+
fn fixup_derefs_on_method_receiver_if_necessary(&self,
1574+
method_callee: &MethodCallee) {
14521575
let sig = match ty::get(method_callee.ty).sty {
14531576
ty::ty_bare_fn(ref f) => f.sig.clone(),
14541577
ty::ty_closure(ref f) => f.sig.clone(),
@@ -1485,6 +1608,9 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
14851608
}
14861609
}
14871610

1611+
debug!("fixup_derefs_on_method_receiver_if_necessary: exprs={}",
1612+
exprs.repr(self.tcx()));
1613+
14881614
// Fix up autoderefs and derefs.
14891615
for (i, expr) in exprs.iter().rev().enumerate() {
14901616
// Count autoderefs.
@@ -1500,6 +1626,9 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
15001626
Some(_) | None => 0,
15011627
};
15021628

1629+
debug!("fixup_derefs_on_method_receiver_if_necessary: i={} expr={} autoderef_count={}",
1630+
i, expr.repr(self.tcx()), autoderef_count);
1631+
15031632
if autoderef_count > 0 {
15041633
check::autoderef(self.fcx,
15051634
expr.span,
@@ -1518,24 +1647,59 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
15181647
// Don't retry the first one or we might infinite loop!
15191648
if i != 0 {
15201649
match expr.node {
1521-
ast::ExprIndex(ref base_expr, ref index_expr) => {
1522-
check::try_overloaded_index(
1523-
self.fcx,
1524-
Some(MethodCall::expr(expr.id)),
1525-
*expr,
1650+
ast::ExprIndex(ref base_expr, _) => {
1651+
let mut base_adjustment =
1652+
match self.fcx.inh.adjustments.borrow().find(&base_expr.id) {
1653+
Some(&ty::AdjustDerefRef(ref adr)) => (*adr).clone(),
1654+
None => ty::AutoDerefRef { autoderefs: 0, autoref: None },
1655+
Some(_) => {
1656+
self.tcx().sess.span_bug(
1657+
base_expr.span,
1658+
"unexpected adjustment type");
1659+
}
1660+
};
1661+
1662+
// If this is an overloaded index, the
1663+
// adjustment will include an extra layer of
1664+
// autoref because the method is an &self/&mut
1665+
// self method. We have to peel it off to get
1666+
// the raw adjustment that `try_index_step`
1667+
// expects. This is annoying and horrible. We
1668+
// ought to recode this routine so it doesn't
1669+
// (ab)use the normal type checking paths.
1670+
base_adjustment.autoref = match base_adjustment.autoref {
1671+
None => { None }
1672+
Some(AutoPtr(_, _, None)) => { None }
1673+
Some(AutoPtr(_, _, Some(box r))) => { Some(r) }
1674+
Some(_) => {
1675+
self.tcx().sess.span_bug(
1676+
base_expr.span,
1677+
"unexpected adjustment autoref");
1678+
}
1679+
};
1680+
1681+
let adjusted_base_ty =
1682+
self.fcx.adjust_expr_ty(
15261683
&**base_expr,
1527-
self.fcx.expr_ty(&**base_expr),
1528-
index_expr,
1529-
PreferMutLvalue);
1684+
Some(&ty::AdjustDerefRef(base_adjustment.clone())));
1685+
1686+
check::try_index_step(
1687+
self.fcx,
1688+
MethodCall::expr(expr.id),
1689+
*expr,
1690+
&**base_expr,
1691+
adjusted_base_ty,
1692+
base_adjustment,
1693+
PreferMutLvalue);
15301694
}
15311695
ast::ExprUnary(ast::UnDeref, ref base_expr) => {
15321696
check::try_overloaded_deref(
1533-
self.fcx,
1534-
expr.span,
1535-
Some(MethodCall::expr(expr.id)),
1536-
Some(&**base_expr),
1537-
self.fcx.expr_ty(&**base_expr),
1538-
PreferMutLvalue);
1697+
self.fcx,
1698+
expr.span,
1699+
Some(MethodCall::expr(expr.id)),
1700+
Some(&**base_expr),
1701+
self.fcx.expr_ty(&**base_expr),
1702+
PreferMutLvalue);
15391703
}
15401704
_ => {}
15411705
}
@@ -1623,15 +1787,25 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
16231787
fn replace_late_bound_regions_with_fresh_var<T>(&self, binder_id: ast::NodeId, value: &T) -> T
16241788
where T : TypeFoldable + Repr
16251789
{
1626-
let (_, value) = replace_late_bound_regions(
1627-
self.fcx.tcx(),
1628-
binder_id,
1629-
value,
1630-
|br| self.fcx.infcx().next_region_var(infer::LateBoundRegion(self.span, br)));
1631-
value
1790+
replace_late_bound_regions_with_fresh_var(self.fcx.infcx(), self.span, binder_id, value)
16321791
}
16331792
}
16341793

1794+
fn replace_late_bound_regions_with_fresh_var<T>(infcx: &infer::InferCtxt,
1795+
span: Span,
1796+
binder_id: ast::NodeId,
1797+
value: &T)
1798+
-> T
1799+
where T : TypeFoldable + Repr
1800+
{
1801+
let (_, value) = replace_late_bound_regions(
1802+
infcx.tcx,
1803+
binder_id,
1804+
value,
1805+
|br| infcx.next_region_var(infer::LateBoundRegion(span, br)));
1806+
value
1807+
}
1808+
16351809
fn trait_method(tcx: &ty::ctxt,
16361810
trait_def_id: ast::DefId,
16371811
method_name: ast::Name)

0 commit comments

Comments
 (0)