Clarify args terminology.
The deriving code has inconsistent terminology to describe args. In some places it distinguishes between: - the `&self` arg (if present), versus - all other args. In other places it distinguishes between: - the `&self` arg (if present) and any other arguments with the same type (in practice there is at most one, e.g. in `PartialEq::eq`), versus - all other args. The terms "self_args" and "nonself_args" are sometimes used for the former distinction, and sometimes for the latter. "args" is also sometimes used for "all other args". This commit makes the code consistently uses "self_args"/"nonself_args" for the former and "selflike_args"/"nonselflike_args" for the latter. This change makes the code easier to read. The commit also adds a panic on an impossible path (the `Self_` case) in `extract_arg_details`.
This commit is contained in:
parent
052495d001
commit
32c9ffb9cc
11 changed files with 147 additions and 109 deletions
|
@ -80,7 +80,7 @@ pub fn expand_deriving_clone(
|
||||||
name: sym::clone,
|
name: sym::clone,
|
||||||
generics: Bounds::empty(),
|
generics: Bounds::empty(),
|
||||||
explicit_self: true,
|
explicit_self: true,
|
||||||
args: Vec::new(),
|
nonself_args: Vec::new(),
|
||||||
ret_ty: Self_,
|
ret_ty: Self_,
|
||||||
attributes: attrs,
|
attributes: attrs,
|
||||||
unify_fieldless_variants: false,
|
unify_fieldless_variants: false,
|
||||||
|
|
|
@ -32,7 +32,7 @@ pub fn expand_deriving_eq(
|
||||||
name: sym::assert_receiver_is_total_eq,
|
name: sym::assert_receiver_is_total_eq,
|
||||||
generics: Bounds::empty(),
|
generics: Bounds::empty(),
|
||||||
explicit_self: true,
|
explicit_self: true,
|
||||||
args: vec![],
|
nonself_args: vec![],
|
||||||
ret_ty: Unit,
|
ret_ty: Unit,
|
||||||
attributes: attrs,
|
attributes: attrs,
|
||||||
unify_fieldless_variants: true,
|
unify_fieldless_variants: true,
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub fn expand_deriving_ord(
|
||||||
name: sym::cmp,
|
name: sym::cmp,
|
||||||
generics: Bounds::empty(),
|
generics: Bounds::empty(),
|
||||||
explicit_self: true,
|
explicit_self: true,
|
||||||
args: vec![(self_ref(), sym::other)],
|
nonself_args: vec![(self_ref(), sym::other)],
|
||||||
ret_ty: Path(path_std!(cmp::Ordering)),
|
ret_ty: Path(path_std!(cmp::Ordering)),
|
||||||
attributes: attrs,
|
attributes: attrs,
|
||||||
unify_fieldless_variants: true,
|
unify_fieldless_variants: true,
|
||||||
|
|
|
@ -69,7 +69,7 @@ pub fn expand_deriving_partial_eq(
|
||||||
name: $name,
|
name: $name,
|
||||||
generics: Bounds::empty(),
|
generics: Bounds::empty(),
|
||||||
explicit_self: true,
|
explicit_self: true,
|
||||||
args: vec![(self_ref(), sym::other)],
|
nonself_args: vec![(self_ref(), sym::other)],
|
||||||
ret_ty: Path(path_local!(bool)),
|
ret_ty: Path(path_local!(bool)),
|
||||||
attributes: attrs,
|
attributes: attrs,
|
||||||
unify_fieldless_variants: true,
|
unify_fieldless_variants: true,
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub fn expand_deriving_partial_ord(
|
||||||
name: sym::partial_cmp,
|
name: sym::partial_cmp,
|
||||||
generics: Bounds::empty(),
|
generics: Bounds::empty(),
|
||||||
explicit_self: true,
|
explicit_self: true,
|
||||||
args: vec![(self_ref(), sym::other)],
|
nonself_args: vec![(self_ref(), sym::other)],
|
||||||
ret_ty,
|
ret_ty,
|
||||||
attributes: attrs,
|
attributes: attrs,
|
||||||
unify_fieldless_variants: true,
|
unify_fieldless_variants: true,
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub fn expand_deriving_debug(
|
||||||
name: sym::fmt,
|
name: sym::fmt,
|
||||||
generics: Bounds::empty(),
|
generics: Bounds::empty(),
|
||||||
explicit_self: true,
|
explicit_self: true,
|
||||||
args: vec![(fmtr, sym::f)],
|
nonself_args: vec![(fmtr, sym::f)],
|
||||||
ret_ty: Path(path_std!(fmt::Result)),
|
ret_ty: Path(path_std!(fmt::Result)),
|
||||||
attributes: Vec::new(),
|
attributes: Vec::new(),
|
||||||
unify_fieldless_variants: false,
|
unify_fieldless_variants: false,
|
||||||
|
@ -53,7 +53,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
|
||||||
// We want to make sure we have the ctxt set so that we can use unstable methods
|
// We want to make sure we have the ctxt set so that we can use unstable methods
|
||||||
let span = cx.with_def_site_ctxt(span);
|
let span = cx.with_def_site_ctxt(span);
|
||||||
let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked));
|
let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked));
|
||||||
let fmt = substr.nonself_args[0].clone();
|
let fmt = substr.nonselflike_args[0].clone();
|
||||||
|
|
||||||
// Struct and tuples are similar enough that we use the same code for both,
|
// Struct and tuples are similar enough that we use the same code for both,
|
||||||
// with some extra pieces for structs due to the field names.
|
// with some extra pieces for structs due to the field names.
|
||||||
|
|
|
@ -36,7 +36,10 @@ pub fn expand_deriving_rustc_decodable(
|
||||||
)],
|
)],
|
||||||
},
|
},
|
||||||
explicit_self: false,
|
explicit_self: false,
|
||||||
args: vec![(Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut), sym::d)],
|
nonself_args: vec![(
|
||||||
|
Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut),
|
||||||
|
sym::d,
|
||||||
|
)],
|
||||||
ret_ty: Path(Path::new_(
|
ret_ty: Path(Path::new_(
|
||||||
pathvec_std!(result::Result),
|
pathvec_std!(result::Result),
|
||||||
vec![
|
vec![
|
||||||
|
@ -63,7 +66,7 @@ fn decodable_substructure(
|
||||||
substr: &Substructure<'_>,
|
substr: &Substructure<'_>,
|
||||||
krate: Symbol,
|
krate: Symbol,
|
||||||
) -> BlockOrExpr {
|
) -> BlockOrExpr {
|
||||||
let decoder = substr.nonself_args[0].clone();
|
let decoder = substr.nonselflike_args[0].clone();
|
||||||
let recurse = vec![
|
let recurse = vec![
|
||||||
Ident::new(krate, trait_span),
|
Ident::new(krate, trait_span),
|
||||||
Ident::new(sym::Decodable, trait_span),
|
Ident::new(sym::Decodable, trait_span),
|
||||||
|
|
|
@ -34,7 +34,7 @@ pub fn expand_deriving_default(
|
||||||
name: kw::Default,
|
name: kw::Default,
|
||||||
generics: Bounds::empty(),
|
generics: Bounds::empty(),
|
||||||
explicit_self: false,
|
explicit_self: false,
|
||||||
args: Vec::new(),
|
nonself_args: Vec::new(),
|
||||||
ret_ty: Self_,
|
ret_ty: Self_,
|
||||||
attributes: attrs,
|
attributes: attrs,
|
||||||
unify_fieldless_variants: false,
|
unify_fieldless_variants: false,
|
||||||
|
|
|
@ -120,7 +120,10 @@ pub fn expand_deriving_rustc_encodable(
|
||||||
)],
|
)],
|
||||||
},
|
},
|
||||||
explicit_self: true,
|
explicit_self: true,
|
||||||
args: vec![(Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut), sym::s)],
|
nonself_args: vec![(
|
||||||
|
Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut),
|
||||||
|
sym::s,
|
||||||
|
)],
|
||||||
ret_ty: Path(Path::new_(
|
ret_ty: Path(Path::new_(
|
||||||
pathvec_std!(result::Result),
|
pathvec_std!(result::Result),
|
||||||
vec![
|
vec![
|
||||||
|
@ -147,7 +150,7 @@ fn encodable_substructure(
|
||||||
substr: &Substructure<'_>,
|
substr: &Substructure<'_>,
|
||||||
krate: Symbol,
|
krate: Symbol,
|
||||||
) -> BlockOrExpr {
|
) -> BlockOrExpr {
|
||||||
let encoder = substr.nonself_args[0].clone();
|
let encoder = substr.nonselflike_args[0].clone();
|
||||||
// throw an underscore in front to suppress unused variable warnings
|
// throw an underscore in front to suppress unused variable warnings
|
||||||
let blkarg = Ident::new(sym::_e, trait_span);
|
let blkarg = Ident::new(sym::_e, trait_span);
|
||||||
let blkencoder = cx.expr_ident(trait_span, blkarg);
|
let blkencoder = cx.expr_ident(trait_span, blkarg);
|
||||||
|
|
|
@ -227,8 +227,8 @@ pub struct MethodDef<'a> {
|
||||||
/// Is there is a `&self` argument? If not, it is a static function.
|
/// Is there is a `&self` argument? If not, it is a static function.
|
||||||
pub explicit_self: bool,
|
pub explicit_self: bool,
|
||||||
|
|
||||||
/// Arguments other than the self argument
|
/// Arguments other than the self argument.
|
||||||
pub args: Vec<(Ty, Symbol)>,
|
pub nonself_args: Vec<(Ty, Symbol)>,
|
||||||
|
|
||||||
/// Returns type
|
/// Returns type
|
||||||
pub ret_ty: Ty,
|
pub ret_ty: Ty,
|
||||||
|
@ -245,8 +245,8 @@ pub struct MethodDef<'a> {
|
||||||
pub struct Substructure<'a> {
|
pub struct Substructure<'a> {
|
||||||
/// ident of self
|
/// ident of self
|
||||||
pub type_ident: Ident,
|
pub type_ident: Ident,
|
||||||
/// verbatim access to any non-self arguments
|
/// verbatim access to any non-selflike arguments
|
||||||
pub nonself_args: &'a [P<Expr>],
|
pub nonselflike_args: &'a [P<Expr>],
|
||||||
pub fields: &'a SubstructureFields<'a>,
|
pub fields: &'a SubstructureFields<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -782,8 +782,8 @@ impl<'a> TraitDef<'a> {
|
||||||
.methods
|
.methods
|
||||||
.iter()
|
.iter()
|
||||||
.map(|method_def| {
|
.map(|method_def| {
|
||||||
let (explicit_self, self_args, nonself_args, tys) =
|
let (explicit_self, selflike_args, nonselflike_args, nonself_arg_tys) =
|
||||||
method_def.split_self_nonself_args(cx, self, type_ident, generics);
|
method_def.extract_arg_details(cx, self, type_ident, generics);
|
||||||
|
|
||||||
let body = if from_scratch || method_def.is_static() {
|
let body = if from_scratch || method_def.is_static() {
|
||||||
method_def.expand_static_struct_method_body(
|
method_def.expand_static_struct_method_body(
|
||||||
|
@ -791,7 +791,7 @@ impl<'a> TraitDef<'a> {
|
||||||
self,
|
self,
|
||||||
struct_def,
|
struct_def,
|
||||||
type_ident,
|
type_ident,
|
||||||
&nonself_args,
|
&nonselflike_args,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
method_def.expand_struct_method_body(
|
method_def.expand_struct_method_body(
|
||||||
|
@ -799,14 +799,22 @@ impl<'a> TraitDef<'a> {
|
||||||
self,
|
self,
|
||||||
struct_def,
|
struct_def,
|
||||||
type_ident,
|
type_ident,
|
||||||
&self_args,
|
&selflike_args,
|
||||||
&nonself_args,
|
&nonselflike_args,
|
||||||
use_temporaries,
|
use_temporaries,
|
||||||
is_packed,
|
is_packed,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
method_def.create_method(cx, self, type_ident, generics, explicit_self, tys, body)
|
method_def.create_method(
|
||||||
|
cx,
|
||||||
|
self,
|
||||||
|
type_ident,
|
||||||
|
generics,
|
||||||
|
explicit_self,
|
||||||
|
nonself_arg_tys,
|
||||||
|
body,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -831,8 +839,8 @@ impl<'a> TraitDef<'a> {
|
||||||
.methods
|
.methods
|
||||||
.iter()
|
.iter()
|
||||||
.map(|method_def| {
|
.map(|method_def| {
|
||||||
let (explicit_self, self_args, nonself_args, tys) =
|
let (explicit_self, selflike_args, nonselflike_args, nonself_arg_tys) =
|
||||||
method_def.split_self_nonself_args(cx, self, type_ident, generics);
|
method_def.extract_arg_details(cx, self, type_ident, generics);
|
||||||
|
|
||||||
let body = if from_scratch || method_def.is_static() {
|
let body = if from_scratch || method_def.is_static() {
|
||||||
method_def.expand_static_enum_method_body(
|
method_def.expand_static_enum_method_body(
|
||||||
|
@ -840,7 +848,7 @@ impl<'a> TraitDef<'a> {
|
||||||
self,
|
self,
|
||||||
enum_def,
|
enum_def,
|
||||||
type_ident,
|
type_ident,
|
||||||
&nonself_args,
|
&nonselflike_args,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
method_def.expand_enum_method_body(
|
method_def.expand_enum_method_body(
|
||||||
|
@ -848,12 +856,20 @@ impl<'a> TraitDef<'a> {
|
||||||
self,
|
self,
|
||||||
enum_def,
|
enum_def,
|
||||||
type_ident,
|
type_ident,
|
||||||
self_args,
|
selflike_args,
|
||||||
&nonself_args,
|
&nonselflike_args,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
method_def.create_method(cx, self, type_ident, generics, explicit_self, tys, body)
|
method_def.create_method(
|
||||||
|
cx,
|
||||||
|
self,
|
||||||
|
type_ident,
|
||||||
|
generics,
|
||||||
|
explicit_self,
|
||||||
|
nonself_arg_tys,
|
||||||
|
body,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -867,11 +883,11 @@ impl<'a> MethodDef<'a> {
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
trait_: &TraitDef<'_>,
|
trait_: &TraitDef<'_>,
|
||||||
type_ident: Ident,
|
type_ident: Ident,
|
||||||
nonself_args: &[P<Expr>],
|
nonselflike_args: &[P<Expr>],
|
||||||
fields: &SubstructureFields<'_>,
|
fields: &SubstructureFields<'_>,
|
||||||
) -> BlockOrExpr {
|
) -> BlockOrExpr {
|
||||||
let span = trait_.span;
|
let span = trait_.span;
|
||||||
let substructure = Substructure { type_ident, nonself_args, fields };
|
let substructure = Substructure { type_ident, nonselflike_args, fields };
|
||||||
let mut f = self.combine_substructure.borrow_mut();
|
let mut f = self.combine_substructure.borrow_mut();
|
||||||
let f: &mut CombineSubstructureFunc<'_> = &mut *f;
|
let f: &mut CombineSubstructureFunc<'_> = &mut *f;
|
||||||
f(cx, span, &substructure)
|
f(cx, span, &substructure)
|
||||||
|
@ -891,49 +907,52 @@ impl<'a> MethodDef<'a> {
|
||||||
!self.explicit_self
|
!self.explicit_self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_self_nonself_args(
|
// The return value includes:
|
||||||
|
// - explicit_self: The `&self` arg, if present.
|
||||||
|
// - selflike_args: Expressions for `&self` (if present) and also any other
|
||||||
|
// args with the same type (e.g. the `other` arg in `PartialEq::eq`).
|
||||||
|
// - nonselflike_args: Expressions for all the remaining args.
|
||||||
|
// - nonself_arg_tys: Additional information about all the args other than
|
||||||
|
// `&self`.
|
||||||
|
fn extract_arg_details(
|
||||||
&self,
|
&self,
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
trait_: &TraitDef<'_>,
|
trait_: &TraitDef<'_>,
|
||||||
type_ident: Ident,
|
type_ident: Ident,
|
||||||
generics: &Generics,
|
generics: &Generics,
|
||||||
) -> (Option<ast::ExplicitSelf>, Vec<P<Expr>>, Vec<P<Expr>>, Vec<(Ident, P<ast::Ty>)>) {
|
) -> (Option<ast::ExplicitSelf>, Vec<P<Expr>>, Vec<P<Expr>>, Vec<(Ident, P<ast::Ty>)>) {
|
||||||
let mut self_args = Vec::new();
|
let mut selflike_args = Vec::new();
|
||||||
let mut nonself_args = Vec::new();
|
let mut nonselflike_args = Vec::new();
|
||||||
let mut arg_tys = Vec::new();
|
let mut nonself_arg_tys = Vec::new();
|
||||||
let span = trait_.span;
|
let span = trait_.span;
|
||||||
|
|
||||||
let ast_explicit_self = if self.explicit_self {
|
let explicit_self = if self.explicit_self {
|
||||||
let (self_expr, explicit_self) = ty::get_explicit_self(cx, span);
|
let (self_expr, explicit_self) = ty::get_explicit_self(cx, span);
|
||||||
self_args.push(self_expr);
|
selflike_args.push(self_expr);
|
||||||
Some(explicit_self)
|
Some(explicit_self)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
for (ty, name) in self.args.iter() {
|
for (ty, name) in self.nonself_args.iter() {
|
||||||
let ast_ty = ty.to_ty(cx, span, type_ident, generics);
|
let ast_ty = ty.to_ty(cx, span, type_ident, generics);
|
||||||
let ident = Ident::new(*name, span);
|
let ident = Ident::new(*name, span);
|
||||||
arg_tys.push((ident, ast_ty));
|
nonself_arg_tys.push((ident, ast_ty));
|
||||||
|
|
||||||
let arg_expr = cx.expr_ident(span, ident);
|
let arg_expr = cx.expr_ident(span, ident);
|
||||||
|
|
||||||
match *ty {
|
match *ty {
|
||||||
// for static methods, just treat any Self
|
// for static methods, just treat any Self
|
||||||
// arguments as a normal arg
|
// arguments as a normal arg
|
||||||
Self_ if !self.is_static() => {
|
|
||||||
self_args.push(arg_expr);
|
|
||||||
}
|
|
||||||
Ref(ref ty, _) if matches!(**ty, Self_) && !self.is_static() => {
|
Ref(ref ty, _) if matches!(**ty, Self_) && !self.is_static() => {
|
||||||
self_args.push(cx.expr_deref(span, arg_expr))
|
selflike_args.push(cx.expr_deref(span, arg_expr))
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
nonself_args.push(arg_expr);
|
|
||||||
}
|
}
|
||||||
|
Self_ => cx.span_bug(span, "`Self` in non-return position"),
|
||||||
|
_ => nonselflike_args.push(arg_expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(ast_explicit_self, self_args, nonself_args, arg_tys)
|
(explicit_self, selflike_args, nonselflike_args, nonself_arg_tys)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_method(
|
fn create_method(
|
||||||
|
@ -943,7 +962,7 @@ impl<'a> MethodDef<'a> {
|
||||||
type_ident: Ident,
|
type_ident: Ident,
|
||||||
generics: &Generics,
|
generics: &Generics,
|
||||||
explicit_self: Option<ast::ExplicitSelf>,
|
explicit_self: Option<ast::ExplicitSelf>,
|
||||||
arg_types: Vec<(Ident, P<ast::Ty>)>,
|
nonself_arg_tys: Vec<(Ident, P<ast::Ty>)>,
|
||||||
body: BlockOrExpr,
|
body: BlockOrExpr,
|
||||||
) -> P<ast::AssocItem> {
|
) -> P<ast::AssocItem> {
|
||||||
let span = trait_.span;
|
let span = trait_.span;
|
||||||
|
@ -951,12 +970,13 @@ impl<'a> MethodDef<'a> {
|
||||||
let fn_generics = self.generics.to_generics(cx, span, type_ident, generics);
|
let fn_generics = self.generics.to_generics(cx, span, type_ident, generics);
|
||||||
|
|
||||||
let args = {
|
let args = {
|
||||||
let self_args = explicit_self.map(|explicit_self| {
|
let self_arg = explicit_self.map(|explicit_self| {
|
||||||
let ident = Ident::with_dummy_span(kw::SelfLower).with_span_pos(span);
|
let ident = Ident::with_dummy_span(kw::SelfLower).with_span_pos(span);
|
||||||
ast::Param::from_self(ast::AttrVec::default(), explicit_self, ident)
|
ast::Param::from_self(ast::AttrVec::default(), explicit_self, ident)
|
||||||
});
|
});
|
||||||
let nonself_args = arg_types.into_iter().map(|(name, ty)| cx.param(span, name, ty));
|
let nonself_args =
|
||||||
self_args.into_iter().chain(nonself_args).collect()
|
nonself_arg_tys.into_iter().map(|(name, ty)| cx.param(span, name, ty));
|
||||||
|
self_arg.into_iter().chain(nonself_args).collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident);
|
let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident);
|
||||||
|
@ -1024,8 +1044,8 @@ impl<'a> MethodDef<'a> {
|
||||||
trait_: &TraitDef<'b>,
|
trait_: &TraitDef<'b>,
|
||||||
struct_def: &'b VariantData,
|
struct_def: &'b VariantData,
|
||||||
type_ident: Ident,
|
type_ident: Ident,
|
||||||
self_args: &[P<Expr>],
|
selflike_args: &[P<Expr>],
|
||||||
nonself_args: &[P<Expr>],
|
nonselflike_args: &[P<Expr>],
|
||||||
use_temporaries: bool,
|
use_temporaries: bool,
|
||||||
is_packed: bool,
|
is_packed: bool,
|
||||||
) -> BlockOrExpr {
|
) -> BlockOrExpr {
|
||||||
|
@ -1033,9 +1053,9 @@ impl<'a> MethodDef<'a> {
|
||||||
let span = trait_.span;
|
let span = trait_.span;
|
||||||
let mut patterns = Vec::new();
|
let mut patterns = Vec::new();
|
||||||
|
|
||||||
for (i, self_arg) in self_args.iter().enumerate() {
|
for (i, selflike_arg) in selflike_args.iter().enumerate() {
|
||||||
let ident_exprs = if !is_packed {
|
let ident_exprs = if !is_packed {
|
||||||
trait_.create_struct_field_accesses(cx, self_arg, struct_def)
|
trait_.create_struct_field_accesses(cx, selflike_arg, struct_def)
|
||||||
} else {
|
} else {
|
||||||
// Get the pattern for the let-destructuring.
|
// Get the pattern for the let-destructuring.
|
||||||
//
|
//
|
||||||
|
@ -1084,7 +1104,7 @@ impl<'a> MethodDef<'a> {
|
||||||
cx,
|
cx,
|
||||||
trait_,
|
trait_,
|
||||||
type_ident,
|
type_ident,
|
||||||
nonself_args,
|
nonselflike_args,
|
||||||
&Struct(struct_def, fields),
|
&Struct(struct_def, fields),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1092,8 +1112,10 @@ impl<'a> MethodDef<'a> {
|
||||||
body
|
body
|
||||||
} else {
|
} else {
|
||||||
// Do the let-destructuring.
|
// Do the let-destructuring.
|
||||||
let mut stmts: Vec<_> = iter::zip(self_args, patterns)
|
let mut stmts: Vec<_> = iter::zip(selflike_args, patterns)
|
||||||
.map(|(arg_expr, pat)| cx.stmt_let_pat(span, pat, arg_expr.clone()))
|
.map(|(selflike_arg_expr, pat)| {
|
||||||
|
cx.stmt_let_pat(span, pat, selflike_arg_expr.clone())
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
stmts.extend(std::mem::take(&mut body.0));
|
stmts.extend(std::mem::take(&mut body.0));
|
||||||
BlockOrExpr(stmts, body.1)
|
BlockOrExpr(stmts, body.1)
|
||||||
|
@ -1106,7 +1128,7 @@ impl<'a> MethodDef<'a> {
|
||||||
trait_: &TraitDef<'_>,
|
trait_: &TraitDef<'_>,
|
||||||
struct_def: &VariantData,
|
struct_def: &VariantData,
|
||||||
type_ident: Ident,
|
type_ident: Ident,
|
||||||
nonself_args: &[P<Expr>],
|
nonselflike_args: &[P<Expr>],
|
||||||
) -> BlockOrExpr {
|
) -> BlockOrExpr {
|
||||||
let summary = trait_.summarise_struct(cx, struct_def);
|
let summary = trait_.summarise_struct(cx, struct_def);
|
||||||
|
|
||||||
|
@ -1114,7 +1136,7 @@ impl<'a> MethodDef<'a> {
|
||||||
cx,
|
cx,
|
||||||
trait_,
|
trait_,
|
||||||
type_ident,
|
type_ident,
|
||||||
nonself_args,
|
nonselflike_args,
|
||||||
&StaticStruct(struct_def, summary),
|
&StaticStruct(struct_def, summary),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1148,7 +1170,7 @@ impl<'a> MethodDef<'a> {
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
/// Creates a match for a tuple of all `self_args`, where either all
|
/// Creates a match for a tuple of all `selflike_args`, where either all
|
||||||
/// variants match, or it falls into a catch-all for when one variant
|
/// variants match, or it falls into a catch-all for when one variant
|
||||||
/// does not match.
|
/// does not match.
|
||||||
///
|
///
|
||||||
|
@ -1161,33 +1183,33 @@ impl<'a> MethodDef<'a> {
|
||||||
/// a simple equality check (for PartialEq).
|
/// a simple equality check (for PartialEq).
|
||||||
///
|
///
|
||||||
/// The catch-all handler is provided access the variant index values
|
/// The catch-all handler is provided access the variant index values
|
||||||
/// for each of the self-args, carried in precomputed variables.
|
/// for each of the selflike_args, carried in precomputed variables.
|
||||||
fn expand_enum_method_body<'b>(
|
fn expand_enum_method_body<'b>(
|
||||||
&self,
|
&self,
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
trait_: &TraitDef<'b>,
|
trait_: &TraitDef<'b>,
|
||||||
enum_def: &'b EnumDef,
|
enum_def: &'b EnumDef,
|
||||||
type_ident: Ident,
|
type_ident: Ident,
|
||||||
mut self_args: Vec<P<Expr>>,
|
mut selflike_args: Vec<P<Expr>>,
|
||||||
nonself_args: &[P<Expr>],
|
nonselflike_args: &[P<Expr>],
|
||||||
) -> BlockOrExpr {
|
) -> BlockOrExpr {
|
||||||
let span = trait_.span;
|
let span = trait_.span;
|
||||||
let variants = &enum_def.variants;
|
let variants = &enum_def.variants;
|
||||||
|
|
||||||
let self_arg_names = iter::once("__self".to_string())
|
let selflike_arg_names = iter::once("__self".to_string())
|
||||||
.chain(
|
.chain(
|
||||||
self_args
|
selflike_args
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|(arg_count, _self_arg)| format!("__arg_{}", arg_count)),
|
.map(|(arg_count, _selflike_arg)| format!("__arg_{}", arg_count)),
|
||||||
)
|
)
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
// The `vi_idents` will be bound, solely in the catch-all, to
|
// The `vi_idents` will be bound, solely in the catch-all, to
|
||||||
// a series of let statements mapping each self_arg to an int
|
// a series of let statements mapping each selflike_arg to an int
|
||||||
// value corresponding to its discriminant.
|
// value corresponding to its discriminant.
|
||||||
let vi_idents = self_arg_names
|
let vi_idents = selflike_arg_names
|
||||||
.iter()
|
.iter()
|
||||||
.map(|name| {
|
.map(|name| {
|
||||||
let vi_suffix = format!("{}_vi", name);
|
let vi_suffix = format!("{}_vi", name);
|
||||||
|
@ -1206,18 +1228,18 @@ impl<'a> MethodDef<'a> {
|
||||||
// (Variant1, Variant1, ...) => Body1
|
// (Variant1, Variant1, ...) => Body1
|
||||||
// (Variant2, Variant2, ...) => Body2
|
// (Variant2, Variant2, ...) => Body2
|
||||||
// ...
|
// ...
|
||||||
// where each tuple has length = self_args.len()
|
// where each tuple has length = selflike_args.len()
|
||||||
let mut match_arms: Vec<ast::Arm> = variants
|
let mut match_arms: Vec<ast::Arm> = variants
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|&(_, v)| !(self.unify_fieldless_variants && v.data.fields().is_empty()))
|
.filter(|&(_, v)| !(self.unify_fieldless_variants && v.data.fields().is_empty()))
|
||||||
.map(|(index, variant)| {
|
.map(|(index, variant)| {
|
||||||
let mk_self_pat = |cx: &mut ExtCtxt<'_>, self_arg_name: &str| {
|
let mk_selflike_pat = |cx: &mut ExtCtxt<'_>, selflike_arg_name: &str| {
|
||||||
let (p, idents) = trait_.create_enum_variant_pattern(
|
let (p, idents) = trait_.create_enum_variant_pattern(
|
||||||
cx,
|
cx,
|
||||||
type_ident,
|
type_ident,
|
||||||
variant,
|
variant,
|
||||||
self_arg_name,
|
selflike_arg_name,
|
||||||
ast::Mutability::Not,
|
ast::Mutability::Not,
|
||||||
);
|
);
|
||||||
(cx.pat(span, PatKind::Ref(p, ast::Mutability::Not)), idents)
|
(cx.pat(span, PatKind::Ref(p, ast::Mutability::Not)), idents)
|
||||||
|
@ -1225,17 +1247,17 @@ impl<'a> MethodDef<'a> {
|
||||||
|
|
||||||
// A single arm has form (&VariantK, &VariantK, ...) => BodyK
|
// A single arm has form (&VariantK, &VariantK, ...) => BodyK
|
||||||
// (see "Final wrinkle" note below for why.)
|
// (see "Final wrinkle" note below for why.)
|
||||||
let mut subpats = Vec::with_capacity(self_arg_names.len());
|
let mut subpats = Vec::with_capacity(selflike_arg_names.len());
|
||||||
let mut self_pats_idents = Vec::with_capacity(self_arg_names.len() - 1);
|
let mut selflike_pats_idents = Vec::with_capacity(selflike_arg_names.len() - 1);
|
||||||
let first_self_pat_idents = {
|
let first_selflike_pat_idents = {
|
||||||
let (p, idents) = mk_self_pat(cx, &self_arg_names[0]);
|
let (p, idents) = mk_selflike_pat(cx, &selflike_arg_names[0]);
|
||||||
subpats.push(p);
|
subpats.push(p);
|
||||||
idents
|
idents
|
||||||
};
|
};
|
||||||
for self_arg_name in &self_arg_names[1..] {
|
for selflike_arg_name in &selflike_arg_names[1..] {
|
||||||
let (p, idents) = mk_self_pat(cx, &self_arg_name);
|
let (p, idents) = mk_selflike_pat(cx, &selflike_arg_name);
|
||||||
subpats.push(p);
|
subpats.push(p);
|
||||||
self_pats_idents.push(idents);
|
selflike_pats_idents.push(idents);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here is the pat = `(&VariantK, &VariantK, ...)`
|
// Here is the pat = `(&VariantK, &VariantK, ...)`
|
||||||
|
@ -1250,24 +1272,24 @@ impl<'a> MethodDef<'a> {
|
||||||
// we are in.
|
// we are in.
|
||||||
|
|
||||||
// All of the Self args have the same variant in these
|
// All of the Self args have the same variant in these
|
||||||
// cases. So we transpose the info in self_pats_idents
|
// cases. So we transpose the info in selflike_pats_idents
|
||||||
// to gather the getter expressions together, in the
|
// to gather the getter expressions together, in the
|
||||||
// form that EnumMatching expects.
|
// form that EnumMatching expects.
|
||||||
|
|
||||||
// The transposition is driven by walking across the
|
// The transposition is driven by walking across the
|
||||||
// arg fields of the variant for the first self pat.
|
// arg fields of the variant for the first selflike pat.
|
||||||
let field_tuples = first_self_pat_idents
|
let field_tuples = first_selflike_pat_idents
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
// For each arg field of self, pull out its getter expr ...
|
// For each arg field of self, pull out its getter expr ...
|
||||||
.map(|(field_index, (span, opt_ident, self_getter_expr, attrs))| {
|
.map(|(field_index, (span, opt_ident, self_getter_expr, attrs))| {
|
||||||
// ... but FieldInfo also wants getter expr
|
// ... but FieldInfo also wants getter expr
|
||||||
// for matching other arguments of Self type;
|
// for matching other arguments of Self type;
|
||||||
// so walk across the *other* self_pats_idents
|
// so walk across the *other* selflike_pats_idents
|
||||||
// and pull out getter for same field in each
|
// and pull out getter for same field in each
|
||||||
// of them (using `field_index` tracked above).
|
// of them (using `field_index` tracked above).
|
||||||
// That is the heart of the transposition.
|
// That is the heart of the transposition.
|
||||||
let others = self_pats_idents
|
let others = selflike_pats_idents
|
||||||
.iter()
|
.iter()
|
||||||
.map(|fields| {
|
.map(|fields| {
|
||||||
let (_, _opt_ident, ref other_getter_expr, _) = fields[field_index];
|
let (_, _opt_ident, ref other_getter_expr, _) = fields[field_index];
|
||||||
|
@ -1298,7 +1320,13 @@ impl<'a> MethodDef<'a> {
|
||||||
// Build up code associated with such a case.
|
// Build up code associated with such a case.
|
||||||
let substructure = EnumMatching(index, variants.len(), variant, field_tuples);
|
let substructure = EnumMatching(index, variants.len(), variant, field_tuples);
|
||||||
let arm_expr = self
|
let arm_expr = self
|
||||||
.call_substructure_method(cx, trait_, type_ident, nonself_args, &substructure)
|
.call_substructure_method(
|
||||||
|
cx,
|
||||||
|
trait_,
|
||||||
|
type_ident,
|
||||||
|
nonselflike_args,
|
||||||
|
&substructure,
|
||||||
|
)
|
||||||
.into_expr(cx, span);
|
.into_expr(cx, span);
|
||||||
|
|
||||||
cx.arm(span, single_pat, arm_expr)
|
cx.arm(span, single_pat, arm_expr)
|
||||||
|
@ -1316,13 +1344,13 @@ impl<'a> MethodDef<'a> {
|
||||||
cx,
|
cx,
|
||||||
trait_,
|
trait_,
|
||||||
type_ident,
|
type_ident,
|
||||||
nonself_args,
|
nonselflike_args,
|
||||||
&substructure,
|
&substructure,
|
||||||
)
|
)
|
||||||
.into_expr(cx, span),
|
.into_expr(cx, span),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ if variants.len() > 1 && self_args.len() > 1 => {
|
_ if variants.len() > 1 && selflike_args.len() > 1 => {
|
||||||
// Since we know that all the arguments will match if we reach
|
// Since we know that all the arguments will match if we reach
|
||||||
// the match expression we add the unreachable intrinsics as the
|
// the match expression we add the unreachable intrinsics as the
|
||||||
// result of the catch all which should help llvm in optimizing it
|
// result of the catch all which should help llvm in optimizing it
|
||||||
|
@ -1349,8 +1377,8 @@ impl<'a> MethodDef<'a> {
|
||||||
// catch-all `_` match, it would trigger the
|
// catch-all `_` match, it would trigger the
|
||||||
// unreachable-pattern error.
|
// unreachable-pattern error.
|
||||||
//
|
//
|
||||||
if variants.len() > 1 && self_args.len() > 1 {
|
if variants.len() > 1 && selflike_args.len() > 1 {
|
||||||
// Build a series of let statements mapping each self_arg
|
// Build a series of let statements mapping each selflike_arg
|
||||||
// to its discriminant value.
|
// to its discriminant value.
|
||||||
//
|
//
|
||||||
// i.e., for `enum E<T> { A, B(1), C(T, T) }`, and a deriving
|
// i.e., for `enum E<T> { A, B(1), C(T, T) }`, and a deriving
|
||||||
|
@ -1365,10 +1393,14 @@ impl<'a> MethodDef<'a> {
|
||||||
// We also build an expression which checks whether all discriminants are equal:
|
// We also build an expression which checks whether all discriminants are equal:
|
||||||
// `__self_vi == __arg_1_vi && __self_vi == __arg_2_vi && ...`
|
// `__self_vi == __arg_1_vi && __self_vi == __arg_2_vi && ...`
|
||||||
let mut discriminant_test = cx.expr_bool(span, true);
|
let mut discriminant_test = cx.expr_bool(span, true);
|
||||||
for (i, (&ident, self_arg)) in iter::zip(&vi_idents, &self_args).enumerate() {
|
for (i, (&ident, selflike_arg)) in iter::zip(&vi_idents, &selflike_args).enumerate() {
|
||||||
let self_addr = cx.expr_addr_of(span, self_arg.clone());
|
let selflike_addr = cx.expr_addr_of(span, selflike_arg.clone());
|
||||||
let variant_value =
|
let variant_value = deriving::call_intrinsic(
|
||||||
deriving::call_intrinsic(cx, span, sym::discriminant_value, vec![self_addr]);
|
cx,
|
||||||
|
span,
|
||||||
|
sym::discriminant_value,
|
||||||
|
vec![selflike_addr],
|
||||||
|
);
|
||||||
let let_stmt = cx.stmt_let(span, false, ident, variant_value);
|
let let_stmt = cx.stmt_let(span, false, ident, variant_value);
|
||||||
index_let_stmts.push(let_stmt);
|
index_let_stmts.push(let_stmt);
|
||||||
|
|
||||||
|
@ -1389,18 +1421,18 @@ impl<'a> MethodDef<'a> {
|
||||||
cx,
|
cx,
|
||||||
trait_,
|
trait_,
|
||||||
type_ident,
|
type_ident,
|
||||||
nonself_args,
|
nonselflike_args,
|
||||||
&catch_all_substructure,
|
&catch_all_substructure,
|
||||||
)
|
)
|
||||||
.into_expr(cx, span);
|
.into_expr(cx, span);
|
||||||
|
|
||||||
// Final wrinkle: the self_args are expressions that deref
|
// Final wrinkle: the selflike_args are expressions that deref
|
||||||
// down to desired places, but we cannot actually deref
|
// down to desired places, but we cannot actually deref
|
||||||
// them when they are fed as r-values into a tuple
|
// them when they are fed as r-values into a tuple
|
||||||
// expression; here add a layer of borrowing, turning
|
// expression; here add a layer of borrowing, turning
|
||||||
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
|
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
|
||||||
self_args.map_in_place(|self_arg| cx.expr_addr_of(span, self_arg));
|
selflike_args.map_in_place(|selflike_arg| cx.expr_addr_of(span, selflike_arg));
|
||||||
let match_arg = cx.expr(span, ast::ExprKind::Tup(self_args));
|
let match_arg = cx.expr(span, ast::ExprKind::Tup(selflike_args));
|
||||||
|
|
||||||
// Lastly we create an expression which branches on all discriminants being equal
|
// Lastly we create an expression which branches on all discriminants being equal
|
||||||
// if discriminant_test {
|
// if discriminant_test {
|
||||||
|
@ -1469,16 +1501,16 @@ impl<'a> MethodDef<'a> {
|
||||||
|
|
||||||
BlockOrExpr(vec![], Some(deriving::call_unreachable(cx, span)))
|
BlockOrExpr(vec![], Some(deriving::call_unreachable(cx, span)))
|
||||||
} else {
|
} else {
|
||||||
// Final wrinkle: the self_args are expressions that deref
|
// Final wrinkle: the selflike_args are expressions that deref
|
||||||
// down to desired places, but we cannot actually deref
|
// down to desired places, but we cannot actually deref
|
||||||
// them when they are fed as r-values into a tuple
|
// them when they are fed as r-values into a tuple
|
||||||
// expression; here add a layer of borrowing, turning
|
// expression; here add a layer of borrowing, turning
|
||||||
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
|
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
|
||||||
self_args.map_in_place(|self_arg| cx.expr_addr_of(span, self_arg));
|
selflike_args.map_in_place(|selflike_arg| cx.expr_addr_of(span, selflike_arg));
|
||||||
let match_arg = if self_args.len() == 1 {
|
let match_arg = if selflike_args.len() == 1 {
|
||||||
self_args.pop().unwrap()
|
selflike_args.pop().unwrap()
|
||||||
} else {
|
} else {
|
||||||
cx.expr(span, ast::ExprKind::Tup(self_args))
|
cx.expr(span, ast::ExprKind::Tup(selflike_args))
|
||||||
};
|
};
|
||||||
BlockOrExpr(vec![], Some(cx.expr_match(span, match_arg, match_arms)))
|
BlockOrExpr(vec![], Some(cx.expr_match(span, match_arg, match_arms)))
|
||||||
}
|
}
|
||||||
|
@ -1490,7 +1522,7 @@ impl<'a> MethodDef<'a> {
|
||||||
trait_: &TraitDef<'_>,
|
trait_: &TraitDef<'_>,
|
||||||
enum_def: &EnumDef,
|
enum_def: &EnumDef,
|
||||||
type_ident: Ident,
|
type_ident: Ident,
|
||||||
nonself_args: &[P<Expr>],
|
nonselflike_args: &[P<Expr>],
|
||||||
) -> BlockOrExpr {
|
) -> BlockOrExpr {
|
||||||
let summary = enum_def
|
let summary = enum_def
|
||||||
.variants
|
.variants
|
||||||
|
@ -1505,7 +1537,7 @@ impl<'a> MethodDef<'a> {
|
||||||
cx,
|
cx,
|
||||||
trait_,
|
trait_,
|
||||||
type_ident,
|
type_ident,
|
||||||
nonself_args,
|
nonselflike_args,
|
||||||
&StaticEnum(enum_def, summary),
|
&StaticEnum(enum_def, summary),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1609,7 +1641,7 @@ impl<'a> TraitDef<'a> {
|
||||||
fn create_struct_field_accesses(
|
fn create_struct_field_accesses(
|
||||||
&self,
|
&self,
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
mut self_arg: &P<Expr>,
|
mut selflike_arg: &P<Expr>,
|
||||||
struct_def: &'a VariantData,
|
struct_def: &'a VariantData,
|
||||||
) -> Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])> {
|
) -> Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])> {
|
||||||
let mut ident_exprs = Vec::new();
|
let mut ident_exprs = Vec::new();
|
||||||
|
@ -1617,8 +1649,8 @@ impl<'a> TraitDef<'a> {
|
||||||
let sp = struct_field.span.with_ctxt(self.span.ctxt());
|
let sp = struct_field.span.with_ctxt(self.span.ctxt());
|
||||||
|
|
||||||
// We don't the need the deref, if there is one.
|
// We don't the need the deref, if there is one.
|
||||||
if let ast::ExprKind::Unary(ast::UnOp::Deref, inner) = &self_arg.kind {
|
if let ast::ExprKind::Unary(ast::UnOp::Deref, inner) = &selflike_arg.kind {
|
||||||
self_arg = inner;
|
selflike_arg = inner;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: we must use `struct_field.span` rather than `span` in the
|
// Note: we must use `struct_field.span` rather than `span` in the
|
||||||
|
@ -1628,7 +1660,7 @@ impl<'a> TraitDef<'a> {
|
||||||
let val = cx.expr(
|
let val = cx.expr(
|
||||||
sp,
|
sp,
|
||||||
ast::ExprKind::Field(
|
ast::ExprKind::Field(
|
||||||
self_arg.clone(),
|
selflike_arg.clone(),
|
||||||
struct_field.ident.unwrap_or_else(|| {
|
struct_field.ident.unwrap_or_else(|| {
|
||||||
Ident::from_str_and_span(&i.to_string(), struct_field.span)
|
Ident::from_str_and_span(&i.to_string(), struct_field.span)
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub fn expand_deriving_hash(
|
||||||
name: sym::hash,
|
name: sym::hash,
|
||||||
generics: Bounds { bounds: vec![(typaram, vec![path_std!(hash::Hasher)])] },
|
generics: Bounds { bounds: vec![(typaram, vec![path_std!(hash::Hasher)])] },
|
||||||
explicit_self: true,
|
explicit_self: true,
|
||||||
args: vec![(Ref(Box::new(Path(arg)), Mutability::Mut), sym::state)],
|
nonself_args: vec![(Ref(Box::new(Path(arg)), Mutability::Mut), sym::state)],
|
||||||
ret_ty: Unit,
|
ret_ty: Unit,
|
||||||
attributes: vec![],
|
attributes: vec![],
|
||||||
unify_fieldless_variants: true,
|
unify_fieldless_variants: true,
|
||||||
|
@ -49,7 +49,7 @@ fn hash_substructure(
|
||||||
trait_span: Span,
|
trait_span: Span,
|
||||||
substr: &Substructure<'_>,
|
substr: &Substructure<'_>,
|
||||||
) -> BlockOrExpr {
|
) -> BlockOrExpr {
|
||||||
let [state_expr] = substr.nonself_args else {
|
let [state_expr] = substr.nonselflike_args else {
|
||||||
cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`");
|
cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`");
|
||||||
};
|
};
|
||||||
let call_hash = |span, thing_expr| {
|
let call_hash = |span, thing_expr| {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue