Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
int
Rust
提交
f2e1a1b5
R
Rust
项目概览
int
/
Rust
接近 1 年 前同步成功
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
Rust
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
f2e1a1b5
编写于
5月 12, 2015
作者:
B
bors
浏览文件
操作
浏览文件
下载
差异文件
Auto merge of #23424 - arielb1:ambiguous-project, r=nikomatsakis
r? @nikomatsakis
上级
a90453a1
2a12e51d
变更
2
显示空白变更内容
内联
并排
Showing
2 changed file
with
123 addition
and
67 deletion
+123
-67
src/librustc/middle/traits/select.rs
src/librustc/middle/traits/select.rs
+103
-67
src/test/run-pass/issue-23336.rs
src/test/run-pass/issue-23336.rs
+20
-0
未找到文件。
src/librustc/middle/traits/select.rs
浏览文件 @
f2e1a1b5
...
...
@@ -117,23 +117,71 @@ pub enum MethodMatchedData {
/// obligation is for `int`. In that case, we drop the impl out of the
/// list. But the other cases are considered *candidates*.
///
/// Candidates can either be definitive or ambiguous. An ambiguous
/// candidate is one that might match or might not, depending on how
/// type variables wind up being resolved. This only occurs during inference.
/// For selection to succeed, there must be exactly one matching
/// candidate. If the obligation is fully known, this is guaranteed
/// by coherence. However, if the obligation contains type parameters
/// or variables, there may be multiple such impls.
///
/// For selection to succeed, there must be exactly one non-ambiguous
/// candidate. Usually, it is not possible to have more than one
/// definitive candidate, due to the coherence rules. However, there is
/// one case where it could occur: if there is a blanket impl for a
/// trait (that is, an impl applied to all T), and a type parameter
/// with a where clause. In that case, we can have a candidate from the
/// where clause and a second candidate from the impl. This is not a
/// problem because coherence guarantees us that the impl which would
/// be used to satisfy the where clause is the same one that we see
/// now. To resolve this issue, therefore, we ignore impls if we find a
/// matching where clause. Part of the reason for this is that where
/// clauses can give additional information (like, the types of output
/// parameters) that would have to be inferred from the impl.
/// It is not a real problem if multiple matching impls exist because
/// of type variables - it just means the obligation isn't sufficiently
/// elaborated. In that case we report an ambiguity, and the caller can
/// try again after more type information has been gathered or report a
/// "type annotations required" error.
///
/// However, with type parameters, this can be a real problem - type
/// parameters don't unify with regular types, but they *can* unify
/// with variables from blanket impls, and (unless we know its bounds
/// will always be satisfied) picking the blanket impl will be wrong
/// for at least *some* substitutions. To make this concrete, if we have
///
/// trait AsDebug { type Out : fmt::Debug; fn debug(self) -> Self::Out; }
/// impl<T: fmt::Debug> AsDebug for T {
/// type Out = T;
/// fn debug(self) -> fmt::Debug { self }
/// }
/// fn foo<T: AsDebug>(t: T) { println!("{:?}", <T as AsDebug>::debug(t)); }
///
/// we can't just use the impl to resolve the <T as AsDebug> obligation
/// - a type from another crate (that doesn't implement fmt::Debug) could
/// implement AsDebug.
///
/// Because where-clauses match the type exactly, multiple clauses can
/// only match if there are unresolved variables, and we can mostly just
/// report this ambiguity in that case. This is still a problem - we can't
/// *do anything* with ambiguities that involve only regions. This is issue
/// #21974.
///
/// If a single where-clause matches and there are no inference
/// variables left, then it definitely matches and we can just select
/// it.
///
/// In fact, we even select the where-clause when the obligation contains
/// inference variables. The can lead to inference making "leaps of logic",
/// for example in this situation:
///
/// pub trait Foo<T> { fn foo(&self) -> T; }
/// impl<T> Foo<()> for T { fn foo(&self) { } }
/// impl Foo<bool> for bool { fn foo(&self) -> bool { *self } }
///
/// pub fn foo<T>(t: T) where T: Foo<bool> {
/// println!("{:?}", <T as Foo<_>>::foo(&t));
/// }
/// fn main() { foo(false); }
///
/// Here the obligation <T as Foo<$0>> can be matched by both the blanket
/// impl and the where-clause. We select the where-clause and unify $0=bool,
/// so the program prints "false". However, if the where-clause is omitted,
/// the blanket impl is selected, we unify $0=(), and the program prints
/// "()".
///
/// Exactly the same issues apply to projection and object candidates, except
/// that we can have both a projection candidate and a where-clause candidate
/// for the same obligation. In that case either would do (except that
/// different "leaps of logic" would occur if inference variables are
/// present), and we just pick the projection. This is, for example,
/// required for associated types to work in default impls, as the bounds
/// are visible both as projection bounds and as where-clauses from the
/// parameter environment.
#[derive(PartialEq,Eq,Debug,Clone)]
enum
SelectionCandidate
<
'tcx
>
{
PhantomFnCandidate
,
...
...
@@ -1350,63 +1398,51 @@ fn winnow_selection<'o>(&mut self,
/// Returns true if `candidate_i` should be dropped in favor of
/// `candidate_j`. Generally speaking we will drop duplicate
/// candidates and prefer where-clause candidates.
/// Returns true if `victim` should be dropped in favor of
/// `other`. Generally speaking we will drop duplicate
/// candidates and prefer where-clause candidates.
///
/// See the comment for "SelectionCandidate" for more details.
fn
candidate_should_be_dropped_in_favor_of
<
'o
>
(
&
mut
self
,
candidate_i
:
&
SelectionCandidate
<
'tcx
>
,
candidate_j
:
&
SelectionCandidate
<
'tcx
>
)
victim
:
&
SelectionCandidate
<
'tcx
>
,
other
:
&
SelectionCandidate
<
'tcx
>
)
->
bool
{
if
candidate_i
==
candidate_j
{
if
victim
==
other
{
return
true
;
}
match
(
candidate_i
,
candidate_j
)
{
(
&
ImplCandidate
(
..
),
&
ParamCandidate
(
..
))
|
(
&
ClosureCandidate
(
..
),
&
ParamCandidate
(
..
))
|
(
&
FnPointerCandidate
(
..
),
&
ParamCandidate
(
..
))
|
(
&
BuiltinObjectCandidate
(
..
),
&
ParamCandidate
(
_
))
|
(
&
BuiltinCandidate
(
..
),
&
ParamCandidate
(
..
))
=>
{
// We basically prefer always prefer to use a
// where-clause over another option. Where clauses
// impose the burden of finding the exact match onto
// the caller. Using an impl in preference of a where
// clause can also lead us to "overspecialize", as in
// #18453.
true
}
(
&
ImplCandidate
(
..
),
&
ObjectCandidate
(
..
))
=>
{
// This means that we are matching an object of type
// `Trait` against the trait `Trait`. In that case, we
// always prefer to use the object vtable over the
// impl. Like a where clause, the impl may or may not
// be the one that is used by the object (because the
// impl may have additional where-clauses that the
// object's source might not meet) -- if it is, using
// the vtable is fine. If it is not, using the vtable
// is good. A win win!
true
}
(
&
DefaultImplCandidate
(
_
),
_
)
=>
{
// Prefer other candidates over default implementations.
match
other
{
&
ObjectCandidate
(
..
)
|
&
ParamCandidate
(
_
)
|
&
ProjectionCandidate
=>
match
victim
{
&
DefaultImplCandidate
(
..
)
=>
{
self
.tcx
()
.sess
.bug
(
"default implementations shouldn't be recorded
\
when there are other valid candidates"
);
}
(
&
ProjectionCandidate
,
&
ParamCandidate
(
_
))
=>
{
// FIXME(#20297) -- this gives where clauses precedent
// over projections. Really these are just two means
// of deducing information (one based on the where
// clauses on the trait definition; one based on those
// on the enclosing scope), and it'd be better to
// integrate them more intelligently. But for now this
// seems ok. If we DON'T give where clauses
// precedence, we run into trouble in default methods,
// where both the projection bounds for `Self::A` and
// the where clauses are in scope
.
&
PhantomFnCandidate
=>
{
self
.tcx
()
.sess
.bug
(
"PhantomFn didn't short-circuit selection"
);
}
&
ImplCandidate
(
..
)
|
&
ClosureCandidate
(
..
)
|
&
FnPointerCandidate
(
..
)
|
&
BuiltinObjectCandidate
(
..
)
|
&
DefaultImplObjectCandidate
(
..
)
|
&
BuiltinCandidate
(
..
)
=>
{
// We have a where-clause so don't go around looking
// for impls
.
true
}
_
=>
{
false
}
&
ObjectCandidate
(
..
)
|
&
ProjectionCandidate
=>
{
// Arbitrarily give param candidates priority
// over projection and object candidates.
true
},
&
ParamCandidate
(
..
)
=>
false
,
&
ErrorCandidate
=>
false
// propagate errors
},
_
=>
false
}
}
...
...
src/test/run-pass/issue-23336.rs
0 → 100644
浏览文件 @
f2e1a1b5
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub
trait
Data
{
fn
doit
(
&
self
)
{}
}
impl
<
T
>
Data
for
T
{}
pub
trait
UnaryLogic
{
type
D
:
Data
;
}
impl
UnaryLogic
for
()
{
type
D
=
i32
;
}
pub
fn
crashes
<
T
:
UnaryLogic
>
(
t
:
T
::
D
)
{
t
.doit
();
}
fn
main
()
{
crashes
::
<
()
>
(
0
);
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录