Support generic associated consts
This commit is contained in:
parent
1447ce78fb
commit
a4f91e5fed
15 changed files with 218 additions and 73 deletions
|
|
@ -657,13 +657,10 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
|||
Some(def::DefConst(did)) |
|
||||
Some(def::DefAssociatedConst(did)) => {
|
||||
if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did,
|
||||
Some(e.id)) {
|
||||
Some(e.id),
|
||||
None) {
|
||||
let inner = v.global_expr(Mode::Const, expr);
|
||||
v.add_qualif(inner);
|
||||
} else {
|
||||
v.tcx.sess.span_bug(e.span,
|
||||
"DefConst or DefAssociatedConst \
|
||||
doesn't point to a constant");
|
||||
}
|
||||
}
|
||||
Some(def::DefLocal(..)) if v.mode == Mode::ConstFn => {
|
||||
|
|
|
|||
|
|
@ -455,7 +455,8 @@ impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
|
|||
let def = self.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def());
|
||||
match def {
|
||||
Some(DefAssociatedConst(did)) |
|
||||
Some(DefConst(did)) => match lookup_const_by_id(self.tcx, did, Some(pat.id)) {
|
||||
Some(DefConst(did)) => match lookup_const_by_id(self.tcx, did,
|
||||
Some(pat.id), None) {
|
||||
Some(const_expr) => {
|
||||
const_expr_to_pat(self.tcx, const_expr, pat.span).map(|new_pat| {
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use front::map as ast_map;
|
|||
use front::map::blocks::FnLikeNode;
|
||||
use middle::cstore::{self, CrateStore, InlinedItem};
|
||||
use middle::{def, infer, subst, traits};
|
||||
use middle::subst::Subst;
|
||||
use middle::def_id::DefId;
|
||||
use middle::pat_util::def_to_path;
|
||||
use middle::ty::{self, Ty};
|
||||
|
|
@ -48,7 +49,7 @@ fn lookup_const<'a>(tcx: &'a ty::ctxt, e: &Expr) -> Option<&'a Expr> {
|
|||
match opt_def {
|
||||
Some(def::DefConst(def_id)) |
|
||||
Some(def::DefAssociatedConst(def_id)) => {
|
||||
lookup_const_by_id(tcx, def_id, Some(e.id))
|
||||
lookup_const_by_id(tcx, def_id, Some(e.id), None)
|
||||
}
|
||||
Some(def::DefVariant(enum_def, variant_def, _)) => {
|
||||
lookup_variant_by_id(tcx, enum_def, variant_def)
|
||||
|
|
@ -88,9 +89,17 @@ fn lookup_variant_by_id<'a>(tcx: &'a ty::ctxt,
|
|||
}
|
||||
}
|
||||
|
||||
/// * `def_id` is the id of the constant.
|
||||
/// * `maybe_ref_id` is the id of the expr referencing the constant.
|
||||
/// * `param_substs` is the monomorphization substitution for the expression.
|
||||
///
|
||||
/// `maybe_ref_id` and `param_substs` are optional and are used for
|
||||
/// finding substitutions in associated constants. This generally
|
||||
/// happens in late/trans const evaluation.
|
||||
pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
|
||||
def_id: DefId,
|
||||
maybe_ref_id: Option<ast::NodeId>)
|
||||
maybe_ref_id: Option<ast::NodeId>,
|
||||
param_substs: Option<&'tcx subst::Substs<'tcx>>)
|
||||
-> Option<&'tcx Expr> {
|
||||
if let Some(node_id) = tcx.map.as_local_node_id(def_id) {
|
||||
match tcx.map.find(node_id) {
|
||||
|
|
@ -111,8 +120,11 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
|
|||
Some(ref_id) => {
|
||||
let trait_id = tcx.trait_of_item(def_id)
|
||||
.unwrap();
|
||||
let substs = tcx.node_id_item_substs(ref_id)
|
||||
.substs;
|
||||
let mut substs = tcx.node_id_item_substs(ref_id)
|
||||
.substs;
|
||||
if let Some(param_substs) = param_substs {
|
||||
substs = substs.subst(tcx, param_substs);
|
||||
}
|
||||
resolve_trait_associated_const(tcx, ti, trait_id,
|
||||
substs)
|
||||
}
|
||||
|
|
@ -158,8 +170,11 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
|
|||
// a trait-associated const if the caller gives us
|
||||
// the expression that refers to it.
|
||||
Some(ref_id) => {
|
||||
let substs = tcx.node_id_item_substs(ref_id)
|
||||
.substs;
|
||||
let mut substs = tcx.node_id_item_substs(ref_id)
|
||||
.substs;
|
||||
if let Some(param_substs) = param_substs {
|
||||
substs = substs.subst(tcx, param_substs);
|
||||
}
|
||||
resolve_trait_associated_const(tcx, ti, trait_id,
|
||||
substs).map(|e| e.id)
|
||||
}
|
||||
|
|
@ -1013,7 +1028,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
|||
_ => (None, None)
|
||||
}
|
||||
} else {
|
||||
(lookup_const_by_id(tcx, def_id, Some(e.id)), None)
|
||||
(lookup_const_by_id(tcx, def_id, Some(e.id), None), None)
|
||||
}
|
||||
}
|
||||
Some(def::DefAssociatedConst(def_id)) => {
|
||||
|
|
@ -1048,7 +1063,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
|||
},
|
||||
}
|
||||
} else {
|
||||
(lookup_const_by_id(tcx, def_id, Some(e.id)), None)
|
||||
(lookup_const_by_id(tcx, def_id, Some(e.id), None), None)
|
||||
}
|
||||
}
|
||||
Some(def::DefVariant(enum_def, variant_def, _)) => {
|
||||
|
|
@ -1260,12 +1275,8 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
|
|||
Ok(None) => {
|
||||
return None
|
||||
}
|
||||
Err(e) => {
|
||||
tcx.sess.span_bug(ti.span,
|
||||
&format!("Encountered error `{:?}` when trying \
|
||||
to select an implementation for \
|
||||
constant trait item reference.",
|
||||
e))
|
||||
Err(_) => {
|
||||
return None
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1273,7 +1284,7 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
|
|||
traits::VtableImpl(ref impl_data) => {
|
||||
match tcx.associated_consts(impl_data.impl_def_id)
|
||||
.iter().find(|ic| ic.name == ti.name) {
|
||||
Some(ic) => lookup_const_by_id(tcx, ic.def_id, None),
|
||||
Some(ic) => lookup_const_by_id(tcx, ic.def_id, None, None),
|
||||
None => match ti.node {
|
||||
hir::ConstTraitItem(_, Some(ref expr)) => Some(&*expr),
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -85,7 +85,8 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
|
|||
let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
|
||||
match def {
|
||||
def::DefConst(def_id) | def::DefAssociatedConst(def_id) =>
|
||||
match const_eval::lookup_const_by_id(self.cx.tcx, def_id, Some(pat.id)) {
|
||||
match const_eval::lookup_const_by_id(self.cx.tcx, def_id,
|
||||
Some(pat.id), None) {
|
||||
Some(const_expr) => {
|
||||
let pat = const_eval::const_expr_to_pat(self.cx.tcx, const_expr,
|
||||
pat.span);
|
||||
|
|
|
|||
|
|
@ -217,7 +217,8 @@ fn const_fn_call<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||
|
||||
pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
def_id: DefId,
|
||||
ref_expr: &hir::Expr)
|
||||
ref_expr: &hir::Expr,
|
||||
param_substs: &'tcx Substs<'tcx>)
|
||||
-> &'tcx hir::Expr {
|
||||
let def_id = inline::maybe_instantiate_inline(ccx, def_id);
|
||||
|
||||
|
|
@ -226,7 +227,7 @@ pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||
"cross crate constant could not be inlined");
|
||||
}
|
||||
|
||||
match const_eval::lookup_const_by_id(ccx.tcx(), def_id, Some(ref_expr.id)) {
|
||||
match const_eval::lookup_const_by_id(ccx.tcx(), def_id, Some(ref_expr.id), Some(param_substs)) {
|
||||
Some(ref expr) => expr,
|
||||
None => {
|
||||
ccx.sess().span_bug(ref_expr.span, "constant item not found")
|
||||
|
|
@ -264,10 +265,12 @@ pub enum TrueConst {
|
|||
|
||||
use self::ConstEvalFailure::*;
|
||||
|
||||
fn get_const_val(ccx: &CrateContext,
|
||||
def_id: DefId,
|
||||
ref_expr: &hir::Expr) -> Result<ValueRef, ConstEvalFailure> {
|
||||
let expr = get_const_expr(ccx, def_id, ref_expr);
|
||||
fn get_const_val<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
def_id: DefId,
|
||||
ref_expr: &hir::Expr,
|
||||
param_substs: &'tcx Substs<'tcx>)
|
||||
-> Result<ValueRef, ConstEvalFailure> {
|
||||
let expr = get_const_expr(ccx, def_id, ref_expr, param_substs);
|
||||
let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
|
||||
match get_const_expr_as_global(ccx, expr, check_const::ConstQualif::empty(),
|
||||
empty_substs, TrueConst::Yes) {
|
||||
|
|
@ -297,7 +300,7 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||
if !ccx.tcx().tables.borrow().adjustments.contains_key(&expr.id) {
|
||||
debug!("get_const_expr_as_global ({:?}): found const {:?}",
|
||||
expr.id, def_id);
|
||||
return get_const_val(ccx, def_id, expr);
|
||||
return get_const_val(ccx, def_id, expr, param_substs);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
|
|
@ -888,7 +891,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
|||
expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val
|
||||
}
|
||||
def::DefConst(def_id) | def::DefAssociatedConst(def_id) => {
|
||||
const_deref_ptr(cx, try!(get_const_val(cx, def_id, e)))
|
||||
const_deref_ptr(cx, try!(get_const_val(cx, def_id, e, param_substs)))
|
||||
}
|
||||
def::DefVariant(enum_did, variant_did, _) => {
|
||||
let vinfo = cx.tcx().lookup_adt_def(enum_did).variant_with_id(variant_did);
|
||||
|
|
|
|||
|
|
@ -165,7 +165,9 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||
hir::ExprPath(..) => {
|
||||
match bcx.def(expr.id) {
|
||||
def::DefConst(did) => {
|
||||
let const_expr = consts::get_const_expr(bcx.ccx(), did, expr);
|
||||
let empty_substs = bcx.tcx().mk_substs(Substs::trans_empty());
|
||||
let const_expr = consts::get_const_expr(bcx.ccx(), did, expr,
|
||||
empty_substs);
|
||||
// Temporarily get cleanup scopes out of the way,
|
||||
// as they require sub-expressions to be contained
|
||||
// inside the current AST scope.
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
},
|
||||
ItemKind::Constant => {
|
||||
let did = inline::maybe_instantiate_inline(bcx.ccx(), did);
|
||||
let expr = const_eval::lookup_const_by_id(bcx.tcx(), did, None)
|
||||
let expr = const_eval::lookup_const_by_id(bcx.tcx(), did, None, Some(substs))
|
||||
.expect("def was const, but lookup_const_by_id failed");
|
||||
// FIXME: this is falling back to translating from HIR. This is not easy to fix,
|
||||
// because we would have somehow adapt const_eval to work on MIR rather than HIR.
|
||||
|
|
|
|||
|
|
@ -3785,35 +3785,8 @@ pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>,
|
|||
def::Def)>
|
||||
{
|
||||
|
||||
// Associated constants can't depend on generic types.
|
||||
fn have_disallowed_generic_consts<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
||||
def: def::Def,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
node_id: ast::NodeId) -> bool {
|
||||
match def {
|
||||
def::DefAssociatedConst(..) => {
|
||||
if ty.has_param_types() || ty.has_self_ty() {
|
||||
span_err!(fcx.sess(), span, E0329,
|
||||
"Associated consts cannot depend \
|
||||
on type parameters or Self.");
|
||||
fcx.write_error(node_id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// If fully resolved already, we don't have to do anything.
|
||||
if path_res.depth == 0 {
|
||||
if let Some(ty) = opt_self_ty {
|
||||
if have_disallowed_generic_consts(fcx, path_res.full_def(), ty,
|
||||
span, node_id) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some((opt_self_ty, &path.segments, path_res.base_def))
|
||||
} else {
|
||||
let mut def = path_res.base_def;
|
||||
|
|
@ -3829,9 +3802,6 @@ pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>,
|
|||
let item_name = item_segment.identifier.name;
|
||||
match method::resolve_ufcs(fcx, span, item_name, ty, node_id) {
|
||||
Ok((def, lp)) => {
|
||||
if have_disallowed_generic_consts(fcx, def, ty, span, node_id) {
|
||||
return None;
|
||||
}
|
||||
// Write back the new resolution.
|
||||
fcx.ccx.tcx.def_map.borrow_mut()
|
||||
.insert(node_id, def::PathResolution {
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@ pub fn build_impl(cx: &DocContext,
|
|||
let did = assoc_const.def_id;
|
||||
let type_scheme = tcx.lookup_item_type(did);
|
||||
let default = if assoc_const.has_value {
|
||||
Some(const_eval::lookup_const_by_id(tcx, did, None)
|
||||
Some(const_eval::lookup_const_by_id(tcx, did, None, None)
|
||||
.unwrap().span.to_src(cx))
|
||||
} else {
|
||||
None
|
||||
|
|
@ -479,7 +479,7 @@ fn build_const(cx: &DocContext, tcx: &ty::ctxt,
|
|||
use rustc::middle::const_eval;
|
||||
use rustc_front::print::pprust;
|
||||
|
||||
let expr = const_eval::lookup_const_by_id(tcx, did, None).unwrap_or_else(|| {
|
||||
let expr = const_eval::lookup_const_by_id(tcx, did, None, None).unwrap_or_else(|| {
|
||||
panic!("expected lookup_const_by_id to succeed for {:?}", did);
|
||||
});
|
||||
debug!("converting constant expr {:?} to snippet", expr);
|
||||
|
|
|
|||
21
src/test/compile-fail/associated-const-array-len.rs
Normal file
21
src/test/compile-fail/associated-const-array-len.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2016 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.
|
||||
|
||||
#![feature(associated_consts)]
|
||||
|
||||
trait Foo {
|
||||
const ID: usize;
|
||||
}
|
||||
|
||||
const X: [i32; <i32 as Foo>::ID] = [0, 1, 2]; //~ ERROR E0250
|
||||
|
||||
fn main() {
|
||||
assert_eq!(1, X);
|
||||
}
|
||||
22
src/test/compile-fail/associated-const-no-item.rs
Normal file
22
src/test/compile-fail/associated-const-no-item.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2016 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.
|
||||
|
||||
#![feature(associated_consts)]
|
||||
|
||||
trait Foo {
|
||||
const ID: i32;
|
||||
}
|
||||
|
||||
const X: i32 = <i32>::ID;
|
||||
//~^ ERROR no associated item named `ID` found for type `i32`
|
||||
|
||||
fn main() {
|
||||
assert_eq!(1, X);
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// 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.
|
||||
|
||||
#![feature(associated_consts)]
|
||||
|
||||
pub enum EFoo { A, B, C, D }
|
||||
|
||||
pub trait Foo {
|
||||
const X: EFoo;
|
||||
}
|
||||
|
||||
struct Abc;
|
||||
impl Foo for Abc {
|
||||
const X: EFoo = EFoo::B;
|
||||
}
|
||||
|
||||
struct Def;
|
||||
impl Foo for Def {
|
||||
const X: EFoo = EFoo::D;
|
||||
}
|
||||
|
||||
pub fn test<A: Foo, B: Foo>(arg: EFoo) {
|
||||
match arg {
|
||||
A::X => println!("A::X"), //~ error: statics cannot be referenced in patterns [E0158]
|
||||
B::X => println!("B::X"), //~ error: statics cannot be referenced in patterns [E0158]
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// 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.
|
||||
|
||||
#![feature(associated_consts)]
|
||||
|
||||
pub trait Foo {
|
||||
const Y: usize;
|
||||
}
|
||||
|
||||
struct Abc;
|
||||
impl Foo for Abc {
|
||||
const Y: usize = 8;
|
||||
}
|
||||
|
||||
struct Def;
|
||||
impl Foo for Def {
|
||||
const Y: usize = 33;
|
||||
}
|
||||
|
||||
pub fn test<A: Foo, B: Foo>() {
|
||||
let _array = [4; <A as Foo>::Y]; //~ error: expected constant integer
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
|
@ -11,16 +11,22 @@
|
|||
#![feature(associated_consts)]
|
||||
|
||||
pub trait Foo {
|
||||
const MIN: i32;
|
||||
|
||||
fn get_min() -> i32 {
|
||||
Self::MIN //~ ERROR E0329
|
||||
}
|
||||
const Y: usize;
|
||||
}
|
||||
|
||||
fn get_min<T: Foo>() -> i32 {
|
||||
T::MIN; //~ ERROR E0329
|
||||
<T as Foo>::MIN //~ ERROR E0329
|
||||
struct Abc;
|
||||
impl Foo for Abc {
|
||||
const Y: usize = 8;
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
struct Def;
|
||||
impl Foo for Def {
|
||||
const Y: usize = 33;
|
||||
}
|
||||
|
||||
pub fn test<A: Foo, B: Foo>() {
|
||||
let _array: [u32; <A as Foo>::Y]; //~ error: the parameter type
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
41
src/test/run-pass/associated-const-type-parameters.rs
Normal file
41
src/test/run-pass/associated-const-type-parameters.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// 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.
|
||||
|
||||
#![feature(associated_consts)]
|
||||
|
||||
trait Foo {
|
||||
const X: i32;
|
||||
fn get_x() -> i32 {
|
||||
Self::X
|
||||
}
|
||||
}
|
||||
|
||||
struct Abc;
|
||||
impl Foo for Abc {
|
||||
const X: i32 = 11;
|
||||
}
|
||||
|
||||
struct Def;
|
||||
impl Foo for Def {
|
||||
const X: i32 = 97;
|
||||
}
|
||||
|
||||
fn sub<A: Foo, B: Foo>() -> i32 {
|
||||
A::X - B::X
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(11, Abc::X);
|
||||
assert_eq!(97, Def::X);
|
||||
assert_eq!(11, Abc::get_x());
|
||||
assert_eq!(97, Def::get_x());
|
||||
assert_eq!(-86, sub::<Abc, Def>());
|
||||
assert_eq!(86, sub::<Def, Abc>());
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue