Skip to content

Commit 454b9d2

Browse files
committed
librustc: Fix expr_use_visitor (and, transitively, the borrow check)
with overloaded calls. This enforces the mutability and borrow restrictions around overloaded calls. Closes #14774. [breaking-change]
1 parent 14c0b3a commit 454b9d2

File tree

2 files changed

+171
-10
lines changed

2 files changed

+171
-10
lines changed

src/librustc/middle/expr_use_visitor.rs

+97-10
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ use middle::def;
1919
use middle::freevars;
2020
use middle::pat_util;
2121
use middle::ty;
22-
use middle::typeck::MethodCall;
22+
use middle::typeck::{MethodCall, MethodObject, MethodOrigin, MethodParam};
23+
use middle::typeck::{MethodStatic};
2324
use middle::typeck;
24-
use syntax::ast;
25-
use syntax::codemap::{Span};
2625
use util::ppaux::Repr;
2726

2827
use std::gc::Gc;
28+
use syntax::ast;
29+
use syntax::codemap::Span;
2930

3031
///////////////////////////////////////////////////////////////////////////
3132
// The Delegate trait
@@ -101,6 +102,74 @@ pub enum MutateMode {
101102
WriteAndRead, // x += y
102103
}
103104

105+
enum OverloadedCallType {
106+
FnOverloadedCall,
107+
FnMutOverloadedCall,
108+
FnOnceOverloadedCall,
109+
}
110+
111+
impl OverloadedCallType {
112+
fn from_trait_id(tcx: &ty::ctxt, trait_id: ast::DefId)
113+
-> OverloadedCallType {
114+
for &(maybe_function_trait, overloaded_call_type) in [
115+
(tcx.lang_items.fn_once_trait(), FnOnceOverloadedCall),
116+
(tcx.lang_items.fn_mut_trait(), FnMutOverloadedCall),
117+
(tcx.lang_items.fn_trait(), FnOverloadedCall)
118+
].iter() {
119+
match maybe_function_trait {
120+
Some(function_trait) if function_trait == trait_id => {
121+
return overloaded_call_type
122+
}
123+
_ => continue,
124+
}
125+
}
126+
127+
tcx.sess.bug("overloaded call didn't map to known function trait")
128+
}
129+
130+
fn from_method_id(tcx: &ty::ctxt, method_id: ast::DefId)
131+
-> OverloadedCallType {
132+
let method_descriptor =
133+
match tcx.methods.borrow_mut().find(&method_id) {
134+
None => {
135+
tcx.sess.bug("overloaded call method wasn't in method \
136+
map")
137+
}
138+
Some(ref method_descriptor) => (*method_descriptor).clone(),
139+
};
140+
let impl_id = match method_descriptor.container {
141+
ty::TraitContainer(_) => {
142+
tcx.sess.bug("statically resolved overloaded call method \
143+
belonged to a trait?!")
144+
}
145+
ty::ImplContainer(impl_id) => impl_id,
146+
};
147+
let trait_ref = match ty::impl_trait_ref(tcx, impl_id) {
148+
None => {
149+
tcx.sess.bug("statically resolved overloaded call impl \
150+
didn't implement a trait?!")
151+
}
152+
Some(ref trait_ref) => (*trait_ref).clone(),
153+
};
154+
OverloadedCallType::from_trait_id(tcx, trait_ref.def_id)
155+
}
156+
157+
fn from_method_origin(tcx: &ty::ctxt, origin: &MethodOrigin)
158+
-> OverloadedCallType {
159+
match *origin {
160+
MethodStatic(def_id) => {
161+
OverloadedCallType::from_method_id(tcx, def_id)
162+
}
163+
MethodParam(ref method_param) => {
164+
OverloadedCallType::from_trait_id(tcx, method_param.trait_id)
165+
}
166+
MethodObject(ref method_object) => {
167+
OverloadedCallType::from_trait_id(tcx, method_object.trait_id)
168+
}
169+
}
170+
}
171+
}
172+
104173
///////////////////////////////////////////////////////////////////////////
105174
// The ExprUseVisitor type
106175
//
@@ -413,19 +482,37 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> {
413482
}
414483
}
415484
_ => {
416-
match self.tcx()
417-
.method_map
418-
.borrow()
419-
.find(&MethodCall::expr(call.id)) {
420-
Some(_) => {
421-
// FIXME(#14774, pcwalton): Implement this.
485+
let overloaded_call_type =
486+
match self.tcx()
487+
.method_map
488+
.borrow()
489+
.find(&MethodCall::expr(call.id)) {
490+
Some(ref method_callee) => {
491+
OverloadedCallType::from_method_origin(
492+
self.tcx(),
493+
&method_callee.origin)
422494
}
423495
None => {
424496
self.tcx().sess.span_bug(
425497
callee.span,
426498
format!("unexpected callee type {}",
427-
callee_ty.repr(self.tcx())).as_slice());
499+
callee_ty.repr(self.tcx())).as_slice())
500+
}
501+
};
502+
match overloaded_call_type {
503+
FnMutOverloadedCall => {
504+
self.borrow_expr(callee,
505+
ty::ReScope(call.id),
506+
ty::MutBorrow,
507+
ClosureInvocation);
508+
}
509+
FnOverloadedCall => {
510+
self.borrow_expr(callee,
511+
ty::ReScope(call.id),
512+
ty::ImmBorrow,
513+
ClosureInvocation);
428514
}
515+
FnOnceOverloadedCall => self.consume_expr(callee),
429516
}
430517
}
431518
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(overloaded_calls)]
12+
13+
use std::ops::{Fn, FnMut, FnOnce};
14+
15+
struct SFn {
16+
x: int,
17+
y: int,
18+
}
19+
20+
impl Fn<(int,),int> for SFn {
21+
fn call(&self, (z,): (int,)) -> int {
22+
self.x * self.y * z
23+
}
24+
}
25+
26+
struct SFnMut {
27+
x: int,
28+
y: int,
29+
}
30+
31+
impl FnMut<(int,),int> for SFnMut {
32+
fn call_mut(&mut self, (z,): (int,)) -> int {
33+
self.x * self.y * z
34+
}
35+
}
36+
37+
struct SFnOnce {
38+
x: String,
39+
}
40+
41+
impl FnOnce<(String,),uint> for SFnOnce {
42+
fn call_once(self, (z,): (String,)) -> uint {
43+
self.x.len() + z.len()
44+
}
45+
}
46+
47+
fn f() {
48+
let mut s = SFn {
49+
x: 1,
50+
y: 2,
51+
};
52+
let sp = &mut s;
53+
s(3); //~ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable
54+
//~^ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable
55+
}
56+
57+
fn g() {
58+
let s = SFnMut {
59+
x: 1,
60+
y: 2,
61+
};
62+
s(3); //~ ERROR cannot borrow immutable local variable `s` as mutable
63+
}
64+
65+
fn h() {
66+
let s = SFnOnce {
67+
x: "hello".to_string(),
68+
};
69+
s(" world".to_string());
70+
s(" world".to_string()); //~ ERROR use of moved value: `s`
71+
}
72+
73+
fn main() {}
74+

0 commit comments

Comments
 (0)