rustc: treat const bodies like fn bodies in middle::region.
This commit is contained in:
parent
8aad3a3524
commit
90af729a97
5 changed files with 105 additions and 152 deletions
|
@ -140,23 +140,6 @@ impl<'this, 'tcx> NestedVisitorMap<'this, 'tcx> {
|
||||||
/// to monitor future changes to `Visitor` in case a new method with a
|
/// to monitor future changes to `Visitor` in case a new method with a
|
||||||
/// new default implementation gets introduced.)
|
/// new default implementation gets introduced.)
|
||||||
pub trait Visitor<'v> : Sized {
|
pub trait Visitor<'v> : Sized {
|
||||||
/// Invokes the suitable visitor method for the given `Node`
|
|
||||||
/// extracted from the hir map.
|
|
||||||
fn visit_hir_map_node(&mut self, node: map::Node<'v>) {
|
|
||||||
match node {
|
|
||||||
map::NodeItem(a) => self.visit_item(a),
|
|
||||||
map::NodeForeignItem(a) => self.visit_foreign_item(a),
|
|
||||||
map::NodeTraitItem(a) => self.visit_trait_item(a),
|
|
||||||
map::NodeImplItem(a) => self.visit_impl_item(a),
|
|
||||||
map::NodeExpr(a) => self.visit_expr(a),
|
|
||||||
map::NodeStmt(a) => self.visit_stmt(a),
|
|
||||||
map::NodeTy(a) => self.visit_ty(a),
|
|
||||||
map::NodePat(a) => self.visit_pat(a),
|
|
||||||
map::NodeBlock(a) => self.visit_block(a),
|
|
||||||
_ => bug!("Visitor::visit_hir_map_node() not yet impl for node `{:?}`", node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Nested items.
|
// Nested items.
|
||||||
|
|
||||||
|
|
|
@ -442,20 +442,21 @@ impl<'hir> Map<'hir> {
|
||||||
self.local_def_id(self.body_owner(id))
|
self.local_def_id(self.body_owner(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a body owner's id, returns the `BodyId` associated with it.
|
/// Given a node id, returns the `BodyId` associated with it,
|
||||||
pub fn body_owned_by(&self, id: NodeId) -> BodyId {
|
/// if the node is a body owner, otherwise returns `None`.
|
||||||
|
pub fn maybe_body_owned_by(&self, id: NodeId) -> Option<BodyId> {
|
||||||
if let Some(entry) = self.find_entry(id) {
|
if let Some(entry) = self.find_entry(id) {
|
||||||
if let Some(body_id) = entry.associated_body() {
|
if let Some(body_id) = entry.associated_body() {
|
||||||
// For item-like things and closures, the associated
|
// For item-like things and closures, the associated
|
||||||
// body has its own distinct id, and that is returned
|
// body has its own distinct id, and that is returned
|
||||||
// by `associated_body`.
|
// by `associated_body`.
|
||||||
body_id
|
Some(body_id)
|
||||||
} else {
|
} else {
|
||||||
// For some expressions, the expression is its own body.
|
// For some expressions, the expression is its own body.
|
||||||
if let EntryExpr(_, expr) = entry {
|
if let EntryExpr(_, expr) = entry {
|
||||||
BodyId { node_id: expr.id }
|
Some(BodyId { node_id: expr.id })
|
||||||
} else {
|
} else {
|
||||||
span_bug!(self.span(id), "id `{}` has no associated body: {:?}", id, entry);
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -463,6 +464,14 @@ impl<'hir> Map<'hir> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given a body owner's id, returns the `BodyId` associated with it.
|
||||||
|
pub fn body_owned_by(&self, id: NodeId) -> BodyId {
|
||||||
|
self.maybe_body_owned_by(id).unwrap_or_else(|| {
|
||||||
|
span_bug!(self.span(id), "body_owned_by: {} has no associated body",
|
||||||
|
self.node_to_string(id));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ty_param_owner(&self, id: NodeId) -> NodeId {
|
pub fn ty_param_owner(&self, id: NodeId) -> NodeId {
|
||||||
match self.get(id) {
|
match self.get(id) {
|
||||||
NodeItem(&Item { node: ItemTrait(..), .. }) => id,
|
NodeItem(&Item { node: ItemTrait(..), .. }) => id,
|
||||||
|
|
|
@ -24,14 +24,16 @@ use std::mem;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use serialize;
|
use serialize;
|
||||||
use syntax::codemap;
|
use syntax::codemap;
|
||||||
use syntax::ast::{self, NodeId};
|
use syntax::ast;
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
use ty::TyCtxt;
|
use ty::TyCtxt;
|
||||||
use ty::maps::Providers;
|
use ty::maps::Providers;
|
||||||
|
|
||||||
use hir; use hir::def_id::DefId;
|
use hir;
|
||||||
use hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap};
|
use hir::def_id::DefId;
|
||||||
use hir::{Block, Item, FnDecl, Arm, Pat, PatKind, Stmt, Expr, Local};
|
use hir::intravisit::{self, Visitor, NestedVisitorMap};
|
||||||
|
use hir::{Block, Arm, Pat, PatKind, Stmt, Expr, Local};
|
||||||
|
use mir::transform::MirSource;
|
||||||
|
|
||||||
pub type CodeExtent<'tcx> = &'tcx CodeExtentData;
|
pub type CodeExtent<'tcx> = &'tcx CodeExtentData;
|
||||||
|
|
||||||
|
@ -811,7 +813,17 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
intravisit::walk_expr(visitor, expr);
|
match expr.node {
|
||||||
|
// Manually recurse over closures, because they are the only
|
||||||
|
// case of nested bodies that share the parent environment.
|
||||||
|
hir::ExprClosure(.., body, _) => {
|
||||||
|
let body = visitor.tcx.hir.body(body);
|
||||||
|
visitor.visit_body(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => intravisit::walk_expr(visitor, expr)
|
||||||
|
}
|
||||||
|
|
||||||
visitor.cx = prev_cx;
|
visitor.cx = prev_cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1041,74 +1053,6 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_item_like<'a, 'tcx, F>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, walk: F)
|
|
||||||
where F: FnOnce(&mut RegionResolutionVisitor<'a, 'tcx>)
|
|
||||||
{
|
|
||||||
// Items create a new outer block scope as far as we're concerned.
|
|
||||||
let prev_cx = visitor.cx;
|
|
||||||
let prev_ts = mem::replace(&mut visitor.terminating_scopes, NodeSet());
|
|
||||||
visitor.cx = Context {
|
|
||||||
root_id: None,
|
|
||||||
var_parent: None,
|
|
||||||
parent: None,
|
|
||||||
};
|
|
||||||
walk(visitor);
|
|
||||||
visitor.cx = prev_cx;
|
|
||||||
visitor.terminating_scopes = prev_ts;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_fn<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
|
|
||||||
kind: FnKind<'tcx>,
|
|
||||||
decl: &'tcx hir::FnDecl,
|
|
||||||
body_id: hir::BodyId,
|
|
||||||
sp: Span,
|
|
||||||
id: ast::NodeId) {
|
|
||||||
visitor.cx.parent = Some(visitor.new_code_extent(
|
|
||||||
CodeExtentData::CallSiteScope { fn_id: id, body_id: body_id.node_id }));
|
|
||||||
|
|
||||||
debug!("region::resolve_fn(id={:?}, \
|
|
||||||
span={:?}, \
|
|
||||||
body.id={:?}, \
|
|
||||||
cx.parent={:?})",
|
|
||||||
id,
|
|
||||||
visitor.tcx.sess.codemap().span_to_string(sp),
|
|
||||||
body_id,
|
|
||||||
visitor.cx.parent);
|
|
||||||
|
|
||||||
let fn_decl_scope = visitor.new_code_extent(
|
|
||||||
CodeExtentData::ParameterScope { fn_id: id, body_id: body_id.node_id });
|
|
||||||
|
|
||||||
if let Some(root_id) = visitor.cx.root_id {
|
|
||||||
visitor.region_maps.record_fn_parent(body_id.node_id, root_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
let outer_cx = visitor.cx;
|
|
||||||
let outer_ts = mem::replace(&mut visitor.terminating_scopes, NodeSet());
|
|
||||||
visitor.terminating_scopes.insert(body_id.node_id);
|
|
||||||
|
|
||||||
// The arguments and `self` are parented to the fn.
|
|
||||||
visitor.cx = Context {
|
|
||||||
root_id: Some(body_id.node_id),
|
|
||||||
parent: None,
|
|
||||||
var_parent: Some(fn_decl_scope),
|
|
||||||
};
|
|
||||||
|
|
||||||
intravisit::walk_fn_decl(visitor, decl);
|
|
||||||
intravisit::walk_fn_kind(visitor, kind);
|
|
||||||
|
|
||||||
// The body of the every fn is a root scope.
|
|
||||||
visitor.cx = Context {
|
|
||||||
root_id: Some(body_id.node_id),
|
|
||||||
parent: Some(fn_decl_scope),
|
|
||||||
var_parent: Some(fn_decl_scope),
|
|
||||||
};
|
|
||||||
visitor.visit_nested_body(body_id);
|
|
||||||
|
|
||||||
// Restore context we had at the start.
|
|
||||||
visitor.cx = outer_cx;
|
|
||||||
visitor.terminating_scopes = outer_ts;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'tcx> RegionResolutionVisitor<'a, 'tcx> {
|
impl<'a, 'tcx> RegionResolutionVisitor<'a, 'tcx> {
|
||||||
pub fn intern_code_extent(&mut self,
|
pub fn intern_code_extent(&mut self,
|
||||||
data: CodeExtentData,
|
data: CodeExtentData,
|
||||||
|
@ -1152,29 +1096,57 @@ impl<'a, 'tcx> RegionResolutionVisitor<'a, 'tcx> {
|
||||||
|
|
||||||
impl<'a, 'tcx> Visitor<'tcx> for RegionResolutionVisitor<'a, 'tcx> {
|
impl<'a, 'tcx> Visitor<'tcx> for RegionResolutionVisitor<'a, 'tcx> {
|
||||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||||
NestedVisitorMap::OnlyBodies(&self.map)
|
NestedVisitorMap::None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_block(&mut self, b: &'tcx Block) {
|
fn visit_block(&mut self, b: &'tcx Block) {
|
||||||
resolve_block(self, b);
|
resolve_block(self, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_item(&mut self, i: &'tcx Item) {
|
fn visit_body(&mut self, body: &'tcx hir::Body) {
|
||||||
resolve_item_like(self, |this| intravisit::walk_item(this, i));
|
let body_id = body.id();
|
||||||
|
let owner_id = self.map.body_owner(body_id);
|
||||||
|
|
||||||
|
debug!("visit_body(id={:?}, span={:?}, body.id={:?}, cx.parent={:?})",
|
||||||
|
owner_id,
|
||||||
|
self.tcx.sess.codemap().span_to_string(body.value.span),
|
||||||
|
body_id,
|
||||||
|
self.cx.parent);
|
||||||
|
|
||||||
|
let outer_cx = self.cx;
|
||||||
|
let outer_ts = mem::replace(&mut self.terminating_scopes, NodeSet());
|
||||||
|
|
||||||
|
// Only functions have an outer terminating (drop) scope,
|
||||||
|
// while temporaries in constant initializers are 'static.
|
||||||
|
if let MirSource::Fn(_) = MirSource::from_node(self.tcx, owner_id) {
|
||||||
|
self.terminating_scopes.insert(body_id.node_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) {
|
if let Some(root_id) = self.cx.root_id {
|
||||||
resolve_item_like(self, |this| intravisit::walk_impl_item(this, ii));
|
self.region_maps.record_fn_parent(body_id.node_id, root_id);
|
||||||
|
}
|
||||||
|
self.cx.root_id = Some(body_id.node_id);
|
||||||
|
|
||||||
|
self.cx.parent = Some(self.new_code_extent(
|
||||||
|
CodeExtentData::CallSiteScope { fn_id: owner_id, body_id: body_id.node_id }));
|
||||||
|
self.cx.parent = Some(self.new_code_extent(
|
||||||
|
CodeExtentData::ParameterScope { fn_id: owner_id, body_id: body_id.node_id }));
|
||||||
|
|
||||||
|
// The arguments and `self` are parented to the fn.
|
||||||
|
self.cx.var_parent = self.cx.parent.take();
|
||||||
|
for argument in &body.arguments {
|
||||||
|
self.visit_pat(&argument.pat);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem) {
|
// The body of the every fn is a root scope.
|
||||||
resolve_item_like(self, |this| intravisit::walk_trait_item(this, ti));
|
self.cx.parent = self.cx.var_parent;
|
||||||
|
self.visit_expr(&body.value);
|
||||||
|
|
||||||
|
// Restore context we had at the start.
|
||||||
|
self.cx = outer_cx;
|
||||||
|
self.terminating_scopes = outer_ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_fn(&mut self, fk: FnKind<'tcx>, fd: &'tcx FnDecl,
|
|
||||||
b: hir::BodyId, s: Span, n: NodeId) {
|
|
||||||
resolve_fn(self, fk, fd, b, s, n);
|
|
||||||
}
|
|
||||||
fn visit_arm(&mut self, a: &'tcx Arm) {
|
fn visit_arm(&mut self, a: &'tcx Arm) {
|
||||||
resolve_arm(self, a);
|
resolve_arm(self, a);
|
||||||
}
|
}
|
||||||
|
@ -1192,21 +1164,18 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionResolutionVisitor<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn region_maps<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn_id: DefId)
|
fn region_maps<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
|
||||||
-> Rc<RegionMaps<'tcx>>
|
-> Rc<RegionMaps<'tcx>>
|
||||||
{
|
{
|
||||||
let closure_base_def_id = tcx.closure_base_def_id(fn_id);
|
let closure_base_def_id = tcx.closure_base_def_id(def_id);
|
||||||
if closure_base_def_id != fn_id {
|
if closure_base_def_id != def_id {
|
||||||
return tcx.region_maps(closure_base_def_id);
|
return tcx.region_maps(closure_base_def_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut maps = RegionMaps::new();
|
let mut maps = RegionMaps::new();
|
||||||
|
|
||||||
let fn_node_id = tcx.hir.as_local_node_id(fn_id)
|
let id = tcx.hir.as_local_node_id(def_id).unwrap();
|
||||||
.expect("fn DefId should be for LOCAL_CRATE");
|
if let Some(body) = tcx.hir.maybe_body_owned_by(id) {
|
||||||
let node = tcx.hir.get(fn_node_id);
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut visitor = RegionResolutionVisitor {
|
let mut visitor = RegionResolutionVisitor {
|
||||||
tcx: tcx,
|
tcx: tcx,
|
||||||
region_maps: &mut maps,
|
region_maps: &mut maps,
|
||||||
|
@ -1218,7 +1187,8 @@ fn region_maps<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn_id: DefId)
|
||||||
},
|
},
|
||||||
terminating_scopes: NodeSet(),
|
terminating_scopes: NodeSet(),
|
||||||
};
|
};
|
||||||
visitor.visit_hir_map_node(node);
|
|
||||||
|
visitor.visit_body(tcx.hir.body(body));
|
||||||
}
|
}
|
||||||
|
|
||||||
Rc::new(maps)
|
Rc::new(maps)
|
||||||
|
|
|
@ -1238,7 +1238,7 @@ impl<'a, 'tcx> ParameterEnvironment<'tcx> {
|
||||||
match tcx.hir.find(id) {
|
match tcx.hir.find(id) {
|
||||||
Some(hir_map::NodeImplItem(ref impl_item)) => {
|
Some(hir_map::NodeImplItem(ref impl_item)) => {
|
||||||
match impl_item.node {
|
match impl_item.node {
|
||||||
hir::ImplItemKind::Type(_) | hir::ImplItemKind::Const(..) => {
|
hir::ImplItemKind::Type(_) => {
|
||||||
// associated types don't have their own entry (for some reason),
|
// associated types don't have their own entry (for some reason),
|
||||||
// so for now just grab environment for the impl
|
// so for now just grab environment for the impl
|
||||||
let impl_id = tcx.hir.get_parent(id);
|
let impl_id = tcx.hir.get_parent(id);
|
||||||
|
@ -1247,7 +1247,8 @@ impl<'a, 'tcx> ParameterEnvironment<'tcx> {
|
||||||
impl_def_id,
|
impl_def_id,
|
||||||
Some(tcx.item_extent(id)))
|
Some(tcx.item_extent(id)))
|
||||||
}
|
}
|
||||||
hir::ImplItemKind::Method(_, ref body) => {
|
hir::ImplItemKind::Const(_, body) |
|
||||||
|
hir::ImplItemKind::Method(_, body) => {
|
||||||
tcx.construct_parameter_environment(
|
tcx.construct_parameter_environment(
|
||||||
impl_item.span,
|
impl_item.span,
|
||||||
tcx.hir.local_def_id(id),
|
tcx.hir.local_def_id(id),
|
||||||
|
@ -1257,56 +1258,37 @@ impl<'a, 'tcx> ParameterEnvironment<'tcx> {
|
||||||
}
|
}
|
||||||
Some(hir_map::NodeTraitItem(trait_item)) => {
|
Some(hir_map::NodeTraitItem(trait_item)) => {
|
||||||
match trait_item.node {
|
match trait_item.node {
|
||||||
hir::TraitItemKind::Type(..) | hir::TraitItemKind::Const(..) => {
|
hir::TraitItemKind::Type(..) |
|
||||||
// associated types don't have their own entry (for some reason),
|
hir::TraitItemKind::Const(_, None) |
|
||||||
// so for now just grab environment for the trait
|
hir::TraitItemKind::Method(_, hir::TraitMethod::Required(_))=> {
|
||||||
let trait_id = tcx.hir.get_parent(id);
|
|
||||||
let trait_def_id = tcx.hir.local_def_id(trait_id);
|
|
||||||
tcx.construct_parameter_environment(trait_item.span,
|
tcx.construct_parameter_environment(trait_item.span,
|
||||||
trait_def_id,
|
tcx.hir.local_def_id(id),
|
||||||
Some(tcx.item_extent(id)))
|
Some(tcx.item_extent(id)))
|
||||||
}
|
}
|
||||||
hir::TraitItemKind::Method(_, ref body) => {
|
hir::TraitItemKind::Const(_, Some(body)) |
|
||||||
// Use call-site for extent (unless this is a
|
hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(body)) => {
|
||||||
// trait method with no default; then fallback
|
|
||||||
// to the method id).
|
|
||||||
let extent = if let hir::TraitMethod::Provided(body_id) = *body {
|
|
||||||
// default impl: use call_site extent as free_id_outlive bound.
|
|
||||||
tcx.call_site_extent(id, body_id.node_id)
|
|
||||||
} else {
|
|
||||||
// no default impl: use item extent as free_id_outlive bound.
|
|
||||||
tcx.item_extent(id)
|
|
||||||
};
|
|
||||||
tcx.construct_parameter_environment(
|
tcx.construct_parameter_environment(
|
||||||
trait_item.span,
|
trait_item.span,
|
||||||
tcx.hir.local_def_id(id),
|
tcx.hir.local_def_id(id),
|
||||||
Some(extent))
|
Some(tcx.call_site_extent(id, body.node_id)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(hir_map::NodeItem(item)) => {
|
Some(hir_map::NodeItem(item)) => {
|
||||||
match item.node {
|
match item.node {
|
||||||
hir::ItemFn(.., body_id) => {
|
hir::ItemConst(_, body) |
|
||||||
// We assume this is a function.
|
hir::ItemStatic(.., body) |
|
||||||
let fn_def_id = tcx.hir.local_def_id(id);
|
hir::ItemFn(.., body) => {
|
||||||
|
|
||||||
tcx.construct_parameter_environment(
|
tcx.construct_parameter_environment(
|
||||||
item.span,
|
item.span,
|
||||||
fn_def_id,
|
tcx.hir.local_def_id(id),
|
||||||
Some(tcx.call_site_extent(id, body_id.node_id)))
|
Some(tcx.call_site_extent(id, body.node_id)))
|
||||||
}
|
}
|
||||||
hir::ItemEnum(..) |
|
hir::ItemEnum(..) |
|
||||||
hir::ItemStruct(..) |
|
hir::ItemStruct(..) |
|
||||||
hir::ItemUnion(..) |
|
hir::ItemUnion(..) |
|
||||||
hir::ItemTy(..) |
|
hir::ItemTy(..) |
|
||||||
hir::ItemImpl(..) |
|
hir::ItemImpl(..) |
|
||||||
hir::ItemConst(..) |
|
|
||||||
hir::ItemStatic(..) => {
|
|
||||||
let def_id = tcx.hir.local_def_id(id);
|
|
||||||
tcx.construct_parameter_environment(item.span,
|
|
||||||
def_id,
|
|
||||||
Some(tcx.item_extent(id)))
|
|
||||||
}
|
|
||||||
hir::ItemTrait(..) => {
|
hir::ItemTrait(..) => {
|
||||||
let def_id = tcx.hir.local_def_id(id);
|
let def_id = tcx.hir.local_def_id(id);
|
||||||
tcx.construct_parameter_environment(item.span,
|
tcx.construct_parameter_environment(item.span,
|
||||||
|
|
|
@ -27,6 +27,12 @@ impl Foo for Def {
|
||||||
const X: i32 = 97;
|
const X: i32 = 97;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Proxy<T>(T);
|
||||||
|
|
||||||
|
impl<T: Foo> Foo for Proxy<T> {
|
||||||
|
const X: i32 = T::X;
|
||||||
|
}
|
||||||
|
|
||||||
fn sub<A: Foo, B: Foo>() -> i32 {
|
fn sub<A: Foo, B: Foo>() -> i32 {
|
||||||
A::X - B::X
|
A::X - B::X
|
||||||
}
|
}
|
||||||
|
@ -38,4 +44,7 @@ fn main() {
|
||||||
assert_eq!(97, Def::get_x());
|
assert_eq!(97, Def::get_x());
|
||||||
assert_eq!(-86, sub::<Abc, Def>());
|
assert_eq!(-86, sub::<Abc, Def>());
|
||||||
assert_eq!(86, sub::<Def, Abc>());
|
assert_eq!(86, sub::<Def, Abc>());
|
||||||
|
assert_eq!(-86, sub::<Proxy<Abc>, Def>());
|
||||||
|
assert_eq!(-86, sub::<Abc, Proxy<Def>>());
|
||||||
|
assert_eq!(86, sub::<Proxy<Def>, Proxy<Abc>>());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue