Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/deriving/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub(crate) mod debug;
pub(crate) mod default;
pub(crate) mod from;
pub(crate) mod hash;
pub(crate) mod reborrow;

#[path = "cmp/eq.rs"]
pub(crate) mod eq;
Expand Down
235 changes: 235 additions & 0 deletions compiler/rustc_builtin_macros/src/deriving/reborrow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
use rustc_ast::{
self as ast, AttrArgs, GenericArg, GenericParamKind, Generics, ItemKind, MetaItem, token,
};
use rustc_errors::E0802;
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_macros::Diagnostic;
use rustc_span::{Ident, Span, Symbol, sym};
use thin_vec::ThinVec;

macro_rules! path {
($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] }
}

pub(crate) fn expand_deriving_reborrow(
cx: &ExtCtxt<'_>,
span: Span,
_mitem: &MetaItem,
item: &Annotatable,
push: &mut dyn FnMut(Annotatable),
_is_const: bool,
) {
let Some((ident, generics)) = struct_def(cx, span, item, sym::Reborrow) else {
return;
};

push_marker_impl(cx, span, ident, generics, sym::Reborrow, Vec::new(), push);
}

pub(crate) fn expand_deriving_coerce_shared(
cx: &ExtCtxt<'_>,
span: Span,
_mitem: &MetaItem,
item: &Annotatable,
push: &mut dyn FnMut(Annotatable),
_is_const: bool,
) {
let Some((ident, generics)) = struct_def(cx, span, item, sym::CoerceShared) else {
return;
};
let Some(target) = coerce_shared_target(cx, span, item) else {
return;
};

push_marker_impl(
cx,
span,
ident,
generics,
sym::CoerceShared,
vec![GenericArg::Type(target)],
push,
);
}

fn struct_def<'a>(
cx: &ExtCtxt<'_>,
span: Span,
item: &'a Annotatable,
trait_name: Symbol,
) -> Option<(Ident, &'a Generics)> {
match item {
Annotatable::Item(item) => match &item.kind {
ItemKind::Struct(ident, generics, _) => Some((*ident, generics)),
ItemKind::Enum(..) => {
cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "enum" });
None
}
ItemKind::Union(..) => {
cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "union" });
None
}
_ => {
cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "item" });
None
}
},
_ => {
cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "item" });
None
}
}
}

fn coerce_shared_target(cx: &ExtCtxt<'_>, span: Span, item: &Annotatable) -> Option<Box<ast::Ty>> {
let Annotatable::Item(item) = item else {
cx.dcx().emit_err(MissingTarget { span });
return None;
};

let mut attrs = item.attrs.iter().filter(|attr| attr.has_name(sym::coerce_shared));
let Some(attr) = attrs.next() else {
cx.dcx().emit_err(MissingTarget { span });
return None;
};
if let Some(duplicate) = attrs.next() {
cx.dcx().emit_err(DuplicateTarget { first: attr.span, duplicate: duplicate.span });
return None;
}

let AttrArgs::Delimited(args) = &attr.get_normal_item().args else {
cx.dcx().emit_err(MalformedTarget { span: attr.span });
return None;
};
if args.delim != token::Delimiter::Parenthesis || args.tokens.is_empty() {
cx.dcx().emit_err(MalformedTarget { span: attr.span });
return None;
}

let mut parser = cx.new_parser_from_tts(args.tokens.clone());
let target = match parser.parse_ty() {
Ok(target) => target,
Err(err) => {
err.cancel();
cx.dcx().emit_err(MalformedTarget { span: attr.span });
return None;
}
};
if parser.token != token::Eof {
cx.dcx().emit_err(MalformedTarget { span: attr.span });
return None;
}

Some(target)
}

fn push_marker_impl(
cx: &ExtCtxt<'_>,
span: Span,
ident: Ident,
generics: &Generics,
trait_name: Symbol,
trait_args: Vec<GenericArg>,
push: &mut dyn FnMut(Annotatable),
) {
let mut trait_parts = path!(span, core::marker);
trait_parts.push(Ident::new(trait_name, span));
let trait_path = cx.path_all(span, true, trait_parts, trait_args);
let trait_ref = cx.trait_ref(trait_path);

let self_params: Vec<_> = generics
.params
.iter()
.map(|param| match param.kind {
GenericParamKind::Lifetime => {
GenericArg::Lifetime(cx.lifetime(param.span(), param.ident))
}
GenericParamKind::Type { .. } => {
GenericArg::Type(cx.ty_ident(param.span(), param.ident))
}
GenericParamKind::Const { .. } => {
GenericArg::Const(cx.const_ident(param.span(), param.ident))
}
})
.collect();
let self_ty = cx.ty_path(cx.path_all(span, false, vec![ident], self_params));

push(Annotatable::Item(cx.item(
span,
thin_vec::thin_vec![cx.attr_word(sym::automatically_derived, span)],
ast::ItemKind::Impl(ast::Impl {
generics: impl_generics(cx, generics),
of_trait: Some(Box::new(ast::TraitImplHeader {
safety: ast::Safety::Default,
polarity: ast::ImplPolarity::Positive,
defaultness: ast::Defaultness::Implicit,
trait_ref,
})),
constness: ast::Const::No,
self_ty,
items: ThinVec::new(),
}),
)));
}

fn impl_generics(cx: &ExtCtxt<'_>, generics: &Generics) -> Generics {
// Rebuild the generic parameter declarations because defaults are allowed on structs but
// rejected on impls. Preserve lifetime, type, and const parameters and their bounds, const
// parameter types, and the where-clause, while omitting type and const defaults.
Generics {
params: generics
.params
.iter()
.map(|param| match &param.kind {
GenericParamKind::Lifetime => {
cx.lifetime_param(param.span(), param.ident, param.bounds.clone())
}
GenericParamKind::Type { default: _ } => {
cx.typaram(param.span(), param.ident, param.bounds.clone(), None)
}
GenericParamKind::Const { ty, span: _, default: _ } => cx.const_param(
param.span(),
param.ident,
param.bounds.clone(),
ty.clone(),
None,
),
})
.collect(),
where_clause: generics.where_clause.clone(),
span: generics.span,
}
}

#[derive(Diagnostic)]
#[diag("`derive({$trait_name})` is only supported for structs, not {$kind}s", code = E0802)]
struct UnsupportedItem {
#[primary_span]
span: Span,
trait_name: Symbol,
kind: &'static str,
}

#[derive(Diagnostic)]
#[diag("`derive(CoerceShared)` requires exactly one `#[coerce_shared(Target)]` attribute", code = E0802)]
struct MissingTarget {
#[primary_span]
span: Span,
}

#[derive(Diagnostic)]
#[diag("duplicate `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`", code = E0802)]
struct DuplicateTarget {
#[primary_span]
duplicate: Span,
#[note("first `#[coerce_shared(Target)]` attribute is here")]
first: Span,
}

#[derive(Diagnostic)]
#[diag("malformed `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`", code = E0802)]
#[note("expected a single target type, for example `#[coerce_shared(Target<'a, T>)]`")]
struct MalformedTarget {
#[primary_span]
span: Span,
}
2 changes: 2 additions & 0 deletions compiler/rustc_builtin_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {

register_derive! {
Clone: clone::expand_deriving_clone,
CoerceShared: reborrow::expand_deriving_coerce_shared,
Copy: bounds::expand_deriving_copy,
ConstParamTy: bounds::expand_deriving_const_param_ty,
Debug: debug::expand_deriving_debug,
Expand All @@ -140,6 +141,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
PartialEq: partial_eq::expand_deriving_partial_eq,
PartialOrd: partial_ord::expand_deriving_partial_ord,
CoercePointee: coerce_pointee::expand_deriving_coerce_pointee,
Reborrow: reborrow::expand_deriving_reborrow,
From: from::expand_deriving_from,
}

Expand Down
17 changes: 14 additions & 3 deletions compiler/rustc_const_eval/src/interpret/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
interp_ok(true)
}
}
// Types that are considered transparent in `unfold_transparent` should also act
// like transparent types here.
ty::Pat(base, _) => self.has_trivial_abi(self.layout_of(base)?),
ty::UnsafeBinder(bound_ty) => {
let ty = self.tcx.instantiate_bound_regions_with_erased(bound_ty.into());
self.has_trivial_abi(self.layout_of(ty)?)
}

ty::Alias(..) => panic!("non-normalized type"),
_ => interp_ok(false),
Expand All @@ -116,8 +123,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
layout: TyAndLayout<'tcx>,
may_unfold: impl Fn(AdtDef<'tcx>) -> bool,
) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
match layout.ty.kind() {
ty::Adt(adt_def, _) if adt_def.repr().transparent() && may_unfold(*adt_def) => {
match *layout.ty.kind() {
ty::Adt(adt_def, _) if adt_def.repr().transparent() && may_unfold(adt_def) => {
assert_matches!(layout.variants, rustc_abi::Variants::Single { .. });
// Look for non-trivial-ABI field(s).
let mut found = None;
Expand All @@ -142,7 +149,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Recurse.
self.unfold_transparent(field, may_unfold)
}
ty::Pat(base, _) => interp_ok(self.layout_of(*base)?),
ty::Pat(base, _) => self.unfold_transparent(self.layout_of(base)?, may_unfold),
ty::UnsafeBinder(bound_ty) => {
let ty = self.tcx.instantiate_bound_regions_with_erased(bound_ty.into());
self.unfold_transparent(self.layout_of(ty)?, may_unfold)
}
// Not a transparent type, no further unfolding.
_ => interp_ok(layout),
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ symbols! {
Clone,
CoercePointee,
CoercePointeeValidated,
CoerceShared,
CoerceUnsized,
Const,
ConstParamTy,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1655,7 +1655,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
};

let mut file = None;
let (msg, span, closure_span) = values
let (msg, mut span, mut closure_span) = values
.and_then(|(predicate, normalized_term, expected_term)| {
self.maybe_detailed_projection_msg(
obligation.cause.span,
Expand All @@ -1676,6 +1676,30 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
None,
)
});

// When the obligation comes from a closure arg and the projection isn't FnOnceOutput
// (which maybe_detailed_projection_msg handles via self_ty), point at the closure's
// return expression and label the closure declaration.
if closure_span.is_none()
&& let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code()
&& let Node::Expr(arg_expr) = self.tcx.hir_node(*arg_hir_id)
&& let hir::ExprKind::Closure(closure) = arg_expr.kind
&& closure.kind == hir::ClosureKind::Closure
{
let body = self.tcx.hir_body(closure.body);
let ret_span = match body.value.kind {
hir::ExprKind::Block(hir::Block { expr: Some(expr), .. }, _) => expr.span,
hir::ExprKind::Block(hir::Block { expr: None, stmts: [.., last], .. }, _) => {
last.span
}
_ => body.value.span,
};
if !closure.fn_decl_span.overlaps(ret_span) {
closure_span = Some(closure.fn_decl_span);
span = ret_span;
}
}

let mut diag = struct_span_code_err!(self.dcx(), span, E0271, "{msg}");
*diag.long_ty_path() = file;
let mut mention_bounds = true;
Expand Down
Loading
Loading