Files
rust/compiler/rustc_ast_lowering/src/item.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1796 lines
73 KiB
Rust
Raw Normal View History

feat: `riscv-interrupt-{m,s}` calling conventions Similar to prior support added for the mips430, avr, and x86 targets this change implements the rough equivalent of clang's [`__attribute__((interrupt))`][clang-attr] for riscv targets, enabling e.g. ```rust static mut CNT: usize = 0; pub extern "riscv-interrupt-m" fn isr_m() { unsafe { CNT += 1; } } ``` to produce highly effective assembly like: ```asm pub extern "riscv-interrupt-m" fn isr_m() { 420003a0: 1141 addi sp,sp,-16 unsafe { CNT += 1; 420003a2: c62a sw a0,12(sp) 420003a4: c42e sw a1,8(sp) 420003a6: 3fc80537 lui a0,0x3fc80 420003aa: 63c52583 lw a1,1596(a0) # 3fc8063c <_ZN12esp_riscv_rt3CNT17hcec3e3a214887d53E.0> 420003ae: 0585 addi a1,a1,1 420003b0: 62b52e23 sw a1,1596(a0) } } 420003b4: 4532 lw a0,12(sp) 420003b6: 45a2 lw a1,8(sp) 420003b8: 0141 addi sp,sp,16 420003ba: 30200073 mret ``` (disassembly via `riscv64-unknown-elf-objdump -C -S --disassemble ./esp32c3-hal/target/riscv32imc-unknown-none-elf/release/examples/gpio_interrupt`) This outcome is superior to hand-coded interrupt routines which, lacking visibility into any non-assembly body of the interrupt handler, have to be very conservative and save the [entire CPU state to the stack frame][full-frame-save]. By instead asking LLVM to only save the registers that it uses, we defer the decision to the tool with the best context: it can more accurately account for the cost of spills if it knows that every additional register used is already at the cost of an implicit spill. At the LLVM level, this is apparently [implemented by] marking every register as "[callee-save]," matching the semantics of an interrupt handler nicely (it has to leave the CPU state just as it found it after its `{m|s}ret`). This approach is not suitable for every interrupt handler, as it makes no attempt to e.g. save the state in a user-accessible stack frame. For a full discussion of those challenges and tradeoffs, please refer to [the interrupt calling conventions RFC][rfc]. Inside rustc, this implementation differs from prior art because LLVM does not expose the "all-saved" function flavor as a calling convention directly, instead preferring to use an attribute that allows for differentiating between "machine-mode" and "superivsor-mode" interrupts. Finally, some effort has been made to guide those who may not yet be aware of the differences between machine-mode and supervisor-mode interrupts as to why no `riscv-interrupt` calling convention is exposed through rustc, and similarly for why `riscv-interrupt-u` makes no appearance (as it would complicate future LLVM upgrades). [clang-attr]: https://clang.llvm.org/docs/AttributeReference.html#interrupt-risc-v [full-frame-save]: https://github.com/esp-rs/esp-riscv-rt/blob/9281af2ecffe13e40992917316f36920c26acaf3/src/lib.rs#L440-L469 [implemented by]: https://github.com/llvm/llvm-project/blob/b7fb2a3fec7c187d58a6d338ab512d9173bca987/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp#L61-L67 [callee-save]: https://github.com/llvm/llvm-project/blob/973f1fe7a8591c7af148e573491ab68cc15b6ecf/llvm/lib/Target/RISCV/RISCVCallingConv.td#L30-L37 [rfc]: https://github.com/rust-lang/rfcs/pull/3246
2023-05-23 15:08:23 -07:00
use super::errors::{InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound};
2021-07-05 22:26:23 +02:00
use super::ResolverAstLoweringExt;
use super::{AstOwner, ImplTraitContext, ImplTraitPosition};
2021-07-13 18:45:20 +02:00
use super::{FnDeclKind, LoweringContext, ParamMode};
use rustc_ast::ptr::P;
2021-07-15 01:18:39 +02:00
use rustc_ast::visit::AssocCtxt;
2020-04-27 23:26:11 +05:30
use rustc_ast::*;
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
2021-07-15 01:18:39 +02:00
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
use rustc_hir::PredicateOrigin;
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::span_bug;
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
use rustc_span::edit_distance::find_best_match_for_name;
2020-04-19 13:00:18 +02:00
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{DesugaringKind, Span, Symbol};
use rustc_target::spec::abi;
use smallvec::{smallvec, SmallVec};
2022-09-08 17:22:52 +10:00
use thin_vec::ThinVec;
use tracing::instrument;
pub(super) struct ItemLowerer<'a, 'hir> {
2021-07-13 18:45:20 +02:00
pub(super) tcx: TyCtxt<'hir>,
pub(super) resolver: &'a mut ResolverAstLowering,
pub(super) ast_index: &'a IndexSlice<LocalDefId, AstOwner<'a>>,
pub(super) owners: &'a mut IndexVec<LocalDefId, hir::MaybeOwner<'hir>>,
2019-08-10 20:13:12 +02:00
}
/// When we have a ty alias we *may* have two where clauses. To give the best diagnostics, we set the span
/// to the where clause that is preferred, if it exists. Otherwise, it sets the span to the other where
/// clause if it exists.
fn add_ty_alias_where_clause(
generics: &mut ast::Generics,
2024-02-19 14:25:33 +01:00
mut where_clauses: TyAliasWhereClauses,
prefer_first: bool,
) {
if !prefer_first {
2024-02-19 14:25:33 +01:00
(where_clauses.before, where_clauses.after) = (where_clauses.after, where_clauses.before);
}
2024-02-19 14:25:33 +01:00
let where_clause =
if where_clauses.before.has_where_token || !where_clauses.after.has_where_token {
where_clauses.before
} else {
where_clauses.after
};
generics.where_clause.has_where_token = where_clause.has_where_token;
generics.where_clause.span = where_clause.span;
}
impl<'a, 'hir> ItemLowerer<'a, 'hir> {
2021-07-15 17:41:48 +02:00
fn with_lctx(
&mut self,
owner: NodeId,
f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>,
) {
2024-06-14 12:16:15 +00:00
let mut lctx = LoweringContext::new(self.tcx, self.resolver, self.ast_index);
2021-07-15 17:41:48 +02:00
lctx.with_hir_id_owner(owner, |lctx| f(lctx));
for (def_id, info) in lctx.children {
let owner = self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom);
debug_assert!(matches!(owner, hir::MaybeOwner::Phantom));
*owner = info;
}
2019-08-10 20:13:12 +02:00
}
pub(super) fn lower_node(&mut self, def_id: LocalDefId) -> hir::MaybeOwner<'hir> {
let owner = self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom);
if let hir::MaybeOwner::Phantom = owner {
2021-07-15 01:18:39 +02:00
let node = self.ast_index[def_id];
match node {
AstOwner::NonOwner => {}
AstOwner::Crate(c) => self.lower_crate(c),
AstOwner::Item(item) => self.lower_item(item),
AstOwner::AssocItem(item, ctxt) => self.lower_assoc_item(item, ctxt),
AstOwner::ForeignItem(item) => self.lower_foreign_item(item),
}
}
self.owners[def_id]
}
#[instrument(level = "debug", skip(self, c))]
2021-07-15 17:41:48 +02:00
fn lower_crate(&mut self, c: &Crate) {
2021-07-18 20:09:20 +02:00
debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID);
2021-07-15 17:41:48 +02:00
self.with_lctx(CRATE_NODE_ID, |lctx| {
let module = lctx.lower_mod(&c.items, &c.spans);
2021-07-15 01:18:39 +02:00
lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs);
hir::OwnerNode::Crate(module)
})
2019-08-10 20:13:12 +02:00
}
#[instrument(level = "debug", skip(self))]
2021-07-15 17:41:48 +02:00
fn lower_item(&mut self, item: &Item) {
self.with_lctx(item.id, |lctx| hir::OwnerNode::Item(lctx.lower_item(item)))
Don't visit foreign function bodies when lowering ast to hir Previously the existence of bodies inside a foreign function block would cause a panic in the hir `NodeCollector` during its collection of crate bodies to compute a crate hash: https://github.com/rust-lang/rust/blob/e59b08e62ea691916d2f063cac5aab4634128022/src/librustc_middle/hir/map/collector.rs#L154-L158 The collector walks the hir tree and creates a map of hir nodes, then attaching bodies in the crate to their owner in the map. For a code like ```rust extern "C" { fn f() { fn g() {} } } ``` The crate bodies include the body of the function `g`. But foreign functions cannot have bodies, and while the parser AST permits a foreign function to have a body, the hir doesn't. This means that the body of `f` is not present in the hir, and so neither is `g`. So when the `NodeCollector` finishes the walking the hir, it has no record of `g`, cannot find an owner for the body of `g` it sees in the crate bodies, and blows up. Why do the crate bodies include the body of `g`? The AST walker has a need a for walking function bodies, and FFIs share the same AST node as functions in other contexts. There are at least two options to fix this: - Don't unwrap the map entry for an hir node in the `NodeCollector` - Modifier the ast->hir lowering visitor to ignore foreign function blocks I don't think the first is preferrable, since we want to know when we can't find a body for an hir node that we thought had one (dropping this information may lead to an invalid hash). So this commit implements the second option. Closes #74120
2020-07-09 19:03:15 -07:00
}
2021-07-15 17:41:48 +02:00
fn lower_assoc_item(&mut self, item: &AssocItem, ctxt: AssocCtxt) {
2021-07-18 20:09:20 +02:00
let def_id = self.resolver.node_id_to_def_id[&item.id];
let parent_id = self.tcx.local_parent(def_id);
2022-04-07 20:54:13 +02:00
let parent_hir = self.lower_node(parent_id).unwrap();
self.with_lctx(item.id, |lctx| lctx.lower_assoc_item(item, ctxt, parent_hir))
2019-08-10 20:13:12 +02:00
}
2020-11-11 21:57:54 +01:00
2021-07-15 17:41:48 +02:00
fn lower_foreign_item(&mut self, item: &ForeignItem) {
self.with_lctx(item.id, |lctx| hir::OwnerNode::ForeignItem(lctx.lower_foreign_item(item)))
2020-11-11 21:57:54 +01:00
}
2019-08-10 20:13:12 +02:00
}
2019-11-29 11:09:23 +01:00
impl<'hir> LoweringContext<'_, 'hir> {
pub(super) fn lower_mod(
&mut self,
items: &[P<Item>],
spans: &ModSpans,
) -> &'hir hir::Mod<'hir> {
self.arena.alloc(hir::Mod {
spans: hir::ModSpans {
inner_span: self.lower_span(spans.inner_span),
inject_use_span: self.lower_span(spans.inject_use_span),
},
2021-07-14 18:54:56 +02:00
item_ids: self.arena.alloc_from_iter(items.iter().flat_map(|x| self.lower_item_ref(x))),
})
2019-08-10 20:13:12 +02:00
}
2021-07-14 18:54:56 +02:00
pub(super) fn lower_item_ref(&mut self, i: &Item) -> SmallVec<[hir::ItemId; 1]> {
let mut node_ids =
smallvec![hir::ItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } }];
if let ItemKind::Use(use_tree) = &i.kind {
self.lower_item_id_use_tree(use_tree, &mut node_ids);
2021-07-14 18:54:56 +02:00
}
2019-08-10 20:13:12 +02:00
node_ids
}
fn lower_item_id_use_tree(&mut self, tree: &UseTree, vec: &mut SmallVec<[hir::ItemId; 1]>) {
match &tree.kind {
UseTreeKind::Nested { items, .. } => {
for &(ref nested, id) in items {
vec.push(hir::ItemId {
owner_id: hir::OwnerId { def_id: self.local_def_id(id) },
});
self.lower_item_id_use_tree(nested, vec);
2019-08-10 20:13:12 +02:00
}
}
UseTreeKind::Simple(..) | UseTreeKind::Glob => {}
2019-08-10 20:13:12 +02:00
}
}
fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> {
2019-08-10 20:13:12 +02:00
let mut ident = i.ident;
2022-02-13 11:30:48 +01:00
let vis_span = self.lower_span(i.vis.span);
let hir_id = self.lower_node_id(i.id);
let attrs = self.lower_attrs(hir_id, &i.attrs);
2022-02-13 11:30:48 +01:00
let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, vis_span, &i.kind);
let item = hir::Item {
owner_id: hir_id.expect_owner(),
ident: self.lower_ident(ident),
kind,
2022-02-13 11:30:48 +01:00
vis_span,
span: self.lower_span(i.span),
};
self.arena.alloc(item)
2019-08-10 20:13:12 +02:00
}
fn lower_item_kind(
&mut self,
span: Span,
2019-08-10 20:13:12 +02:00
id: NodeId,
hir_id: hir::HirId,
2019-08-10 20:13:12 +02:00
ident: &mut Ident,
2021-01-24 17:14:17 +01:00
attrs: Option<&'hir [Attribute]>,
2022-02-13 11:30:48 +01:00
vis_span: Span,
2019-08-10 20:13:12 +02:00
i: &ItemKind,
2019-11-28 19:28:50 +01:00
) -> hir::ItemKind<'hir> {
match i {
ItemKind::ExternCrate(orig_name) => hir::ItemKind::ExternCrate(*orig_name),
ItemKind::Use(use_tree) => {
2019-08-10 20:13:12 +02:00
// Start with an empty prefix.
2022-09-08 17:22:52 +10:00
let prefix = Path { segments: ThinVec::new(), span: use_tree.span, tokens: None };
2019-08-10 20:13:12 +02:00
2022-02-13 11:30:48 +01:00
self.lower_use_tree(use_tree, &prefix, id, vis_span, ident, attrs)
2019-08-10 20:13:12 +02:00
}
ItemKind::Static(box ast::StaticItem { ty: t, safety: _, mutability: m, expr: e }) => {
let (ty, body_id) =
self.lower_const_item(t, span, e.as_deref(), ImplTraitPosition::StaticTy);
hir::ItemKind::Static(ty, *m, body_id)
2019-08-10 20:13:12 +02:00
}
2023-05-04 16:40:57 +02:00
ItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
let (generics, (ty, body_id)) = self.lower_generics(
generics,
Const::No,
2024-06-14 12:16:15 +00:00
false,
2023-05-04 16:40:57 +02:00
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
this.lower_const_item(ty, span, expr.as_deref(), ImplTraitPosition::ConstTy)
},
2023-05-04 16:40:57 +02:00
);
hir::ItemKind::Const(ty, generics, body_id)
2019-08-10 20:13:12 +02:00
}
ItemKind::Fn(box Fn {
sig: FnSig { decl, header, span: fn_sig_span },
generics,
body,
..
}) => {
self.with_new_scopes(*fn_sig_span, |this| {
2019-08-10 20:13:12 +02:00
// Note: we don't need to change the return type from `T` to
// `impl Future<Output = T>` here because lower_body
// only cares about the input argument patterns in the function
// declaration (decl), not the return types.
2023-12-05 21:39:36 +00:00
let coroutine_kind = header.coroutine_kind;
let body_id = this.lower_maybe_coroutine_body(
2024-06-27 14:56:57 -04:00
*fn_sig_span,
span,
hir_id,
decl,
2023-12-05 21:39:36 +00:00
coroutine_kind,
body.as_deref(),
);
2019-08-10 20:13:12 +02:00
let itctx = ImplTraitContext::Universal;
let (generics, decl) =
2024-06-14 12:16:15 +00:00
this.lower_generics(generics, header.constness, false, id, itctx, |this| {
2023-12-05 21:39:36 +00:00
this.lower_fn_decl(
decl,
id,
*fn_sig_span,
FnDeclKind::Fn,
coroutine_kind,
)
});
Use smaller def span for functions Currently, the def span of a funtion encompasses the entire function signature and body. However, this is usually unnecessarily verbose - when we are pointing at an entire function in a diagnostic, we almost always want to point at the signature. The actual contents of the body tends to be irrelevant to the diagnostic we are emitting, and just takes up additional screen space. This commit changes the `def_span` of all function items (freestanding functions, `impl`-block methods, and `trait`-block methods) to be the span of the signature. For example, the function ```rust pub fn foo<T>(val: T) -> T { val } ``` now has a `def_span` corresponding to `pub fn foo<T>(val: T) -> T` (everything before the opening curly brace). Trait methods without a body have a `def_span` which includes the trailing semicolon. For example: ```rust trait Foo { fn bar(); }``` the function definition `Foo::bar` has a `def_span` of `fn bar();` This makes our diagnostic output much shorter, and emphasizes information that is relevant to whatever diagnostic we are reporting. We continue to use the full span (including the body) in a few of places: * MIR building uses the full span when building source scopes. * 'Outlives suggestions' use the full span to sort the diagnostics being emitted. * The `#[rustc_on_unimplemented(enclosing_scope="in this scope")]` attribute points the entire scope body. * The 'unconditional recursion' lint uses the full span to show additional context for the recursive call. All of these cases work only with local items, so we don't need to add anything extra to crate metadata.
2020-08-12 17:02:14 -04:00
let sig = hir::FnSig {
decl,
header: this.lower_fn_header(*header),
span: this.lower_span(*fn_sig_span),
Use smaller def span for functions Currently, the def span of a funtion encompasses the entire function signature and body. However, this is usually unnecessarily verbose - when we are pointing at an entire function in a diagnostic, we almost always want to point at the signature. The actual contents of the body tends to be irrelevant to the diagnostic we are emitting, and just takes up additional screen space. This commit changes the `def_span` of all function items (freestanding functions, `impl`-block methods, and `trait`-block methods) to be the span of the signature. For example, the function ```rust pub fn foo<T>(val: T) -> T { val } ``` now has a `def_span` corresponding to `pub fn foo<T>(val: T) -> T` (everything before the opening curly brace). Trait methods without a body have a `def_span` which includes the trailing semicolon. For example: ```rust trait Foo { fn bar(); }``` the function definition `Foo::bar` has a `def_span` of `fn bar();` This makes our diagnostic output much shorter, and emphasizes information that is relevant to whatever diagnostic we are reporting. We continue to use the full span (including the body) in a few of places: * MIR building uses the full span when building source scopes. * 'Outlives suggestions' use the full span to sort the diagnostics being emitted. * The `#[rustc_on_unimplemented(enclosing_scope="in this scope")]` attribute points the entire scope body. * The 'unconditional recursion' lint uses the full span to show additional context for the recursive call. All of these cases work only with local items, so we don't need to add anything extra to crate metadata.
2020-08-12 17:02:14 -04:00
};
2019-11-07 12:57:52 +01:00
hir::ItemKind::Fn(sig, generics, body_id)
2019-08-10 20:13:12 +02:00
})
}
ItemKind::Mod(_, mod_kind) => match mod_kind {
ModKind::Loaded(items, _, spans) => {
hir::ItemKind::Mod(self.lower_mod(items, spans))
}
ModKind::Unloaded => panic!("`mod` items should have been loaded by now"),
},
ItemKind::ForeignMod(fm) => hir::ItemKind::ForeignMod {
2021-07-08 21:58:05 +02:00
abi: fm.abi.map_or(abi::Abi::FALLBACK, |abi| self.lower_abi(abi)),
items: self
.arena
.alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
},
ItemKind::GlobalAsm(asm) => hir::ItemKind::GlobalAsm(self.lower_inline_asm(span, asm)),
2023-02-22 21:19:42 +00:00
ItemKind::TyAlias(box TyAlias { generics, where_clauses, ty, .. }) => {
// We lower
//
// type Foo = impl Trait
//
// to
//
// type Foo = Foo1
// opaque type Foo1: Trait
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, *where_clauses, true);
let (generics, ty) = self.lower_generics(
&generics,
Const::No,
2024-06-14 12:16:15 +00:00
false,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
2023-02-22 21:19:42 +00:00
|this| match ty {
None => {
let guar = this.dcx().span_delayed_bug(
2023-02-22 21:19:42 +00:00
span,
"expected to lower type alias type, but it was missing",
);
this.arena.alloc(this.ty(span, hir::TyKind::Err(guar)))
}
Some(ty) => this.lower_ty(
ty,
2024-03-06 18:44:55 +00:00
ImplTraitContext::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias {
parent: this.local_def_id(id),
in_assoc_ty: false,
},
2024-03-06 18:44:55 +00:00
fn_kind: None,
},
),
2023-02-22 21:19:42 +00:00
},
);
hir::ItemKind::TyAlias(ty, generics)
}
ItemKind::Enum(enum_definition, generics) => {
let (generics, variants) = self.lower_generics(
generics,
Const::No,
2024-06-14 12:16:15 +00:00
false,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
this.arena.alloc_from_iter(
enum_definition.variants.iter().map(|x| this.lower_variant(x)),
)
},
);
hir::ItemKind::Enum(hir::EnumDef { variants }, generics)
}
ItemKind::Struct(struct_def, generics) => {
let (generics, struct_def) = self.lower_generics(
generics,
Const::No,
2024-06-14 12:16:15 +00:00
false,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| this.lower_variant_data(hir_id, struct_def),
);
hir::ItemKind::Struct(struct_def, generics)
2019-08-10 20:13:12 +02:00
}
ItemKind::Union(vdata, generics) => {
let (generics, vdata) = self.lower_generics(
generics,
Const::No,
2024-06-14 12:16:15 +00:00
false,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| this.lower_variant_data(hir_id, vdata),
);
hir::ItemKind::Union(vdata, generics)
2019-08-10 20:13:12 +02:00
}
ItemKind::Impl(box Impl {
2024-05-17 14:17:48 -03:00
safety,
2019-08-10 20:13:12 +02:00
polarity,
defaultness,
constness,
generics: ast_generics,
of_trait: trait_ref,
self_ty: ty,
items: impl_items,
2021-01-29 08:31:08 +01:00
}) => {
2019-08-10 20:13:12 +02:00
// Lower the "impl header" first. This ordering is important
// for in-band lifetimes! Consider `'a` here:
//
// impl Foo<'a> for u32 {
// fn method(&'a self) { .. }
// }
//
// Because we start by lowering the `Foo<'a> for u32`
// part, we will add `'a` to the list of generics on
// the impl. When we then encounter it later in the
// method, it will not be considered an in-band
// lifetime to be added, but rather a reference to a
// parent lifetime.
let itctx = ImplTraitContext::Universal;
2022-04-07 20:54:13 +02:00
let (generics, (trait_ref, lowered_ty)) =
2024-06-14 12:16:15 +00:00
self.lower_generics(ast_generics, Const::No, false, id, itctx, |this| {
let modifiers = TraitBoundModifiers {
2024-06-14 12:16:15 +00:00
constness: BoundConstness::Never,
asyncness: BoundAsyncness::Normal,
// we don't use this in bound lowering
polarity: BoundPolarity::Positive,
};
2019-08-10 20:13:12 +02:00
let trait_ref = trait_ref.as_ref().map(|trait_ref| {
this.lower_trait_ref(
modifiers,
trait_ref,
ImplTraitContext::Disallowed(ImplTraitPosition::Trait),
)
2019-08-10 20:13:12 +02:00
});
let lowered_ty = this.lower_ty(
ty,
ImplTraitContext::Disallowed(ImplTraitPosition::ImplSelf),
);
2019-08-10 20:13:12 +02:00
(trait_ref, lowered_ty)
});
self.is_in_trait_impl = trait_ref.is_some();
let new_impl_items = self
.arena
.alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item)));
2019-08-10 20:13:12 +02:00
// `defaultness.has_value()` is never called for an `impl`, always `true` in order
// to not cause an assertion failure inside the `lower_defaultness` function.
let has_val = true;
let (defaultness, defaultness_span) = self.lower_defaultness(*defaultness, has_val);
let polarity = match polarity {
ImplPolarity::Positive => ImplPolarity::Positive,
ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(*s)),
};
2022-02-05 15:26:49 +01:00
hir::ItemKind::Impl(self.arena.alloc(hir::Impl {
2024-06-14 12:16:15 +00:00
constness: self.lower_constness(*constness),
safety: self.lower_safety(*safety, hir::Safety::Safe),
polarity,
defaultness,
defaultness_span,
2019-08-10 20:13:12 +02:00
generics,
of_trait: trait_ref,
self_ty: lowered_ty,
items: new_impl_items,
2022-02-05 15:26:49 +01:00
}))
2019-08-10 20:13:12 +02:00
}
2024-05-17 14:17:48 -03:00
ItemKind::Trait(box Trait { is_auto, safety, generics, bounds, items }) => {
let (generics, (safety, items, bounds)) = self.lower_generics(
generics,
2024-06-14 12:16:15 +00:00
Const::No,
false,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
let bounds = this.lower_param_bounds(
bounds,
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
);
let items = this.arena.alloc_from_iter(
items.iter().map(|item| this.lower_trait_item_ref(item)),
);
let safety = this.lower_safety(*safety, hir::Safety::Safe);
2024-05-17 14:17:48 -03:00
(safety, items, bounds)
},
);
2024-05-17 14:17:48 -03:00
hir::ItemKind::Trait(*is_auto, safety, generics, bounds, items)
2019-08-10 20:13:12 +02:00
}
ItemKind::TraitAlias(generics, bounds) => {
let (generics, bounds) = self.lower_generics(
generics,
Const::No,
2024-06-14 12:16:15 +00:00
false,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
this.lower_param_bounds(
bounds,
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
)
},
);
hir::ItemKind::TraitAlias(generics, bounds)
}
ItemKind::MacroDef(MacroDef { body, macro_rules }) => {
let body = P(self.lower_delim_args(body));
2023-12-15 03:34:37 +00:00
let def_id = self.local_def_id(id);
let def_kind = self.tcx.def_kind(def_id);
let DefKind::Macro(macro_kind) = def_kind else {
unreachable!(
"expected DefKind::Macro for macro item, found {}",
def_kind.descr(def_id.to_def_id())
);
};
let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules });
hir::ItemKind::Macro(macro_def, macro_kind)
2021-07-30 23:50:57 -07:00
}
2023-11-26 15:57:31 +03:00
ItemKind::Delegation(box delegation) => {
let delegation_results = self.lower_delegation(delegation, id);
hir::ItemKind::Fn(
delegation_results.sig,
delegation_results.generics,
delegation_results.body_id,
)
}
ItemKind::MacCall(..) | ItemKind::DelegationMac(..) => {
panic!("macros should have been expanded by now")
2019-12-22 17:42:04 -05:00
}
2019-08-10 20:13:12 +02:00
}
}
fn lower_const_item(
&mut self,
ty: &Ty,
span: Span,
body: Option<&Expr>,
impl_trait_position: ImplTraitPosition,
) -> (&'hir hir::Ty<'hir>, hir::BodyId) {
let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(impl_trait_position));
(ty, self.lower_const_body(span, body))
}
#[instrument(level = "debug", skip(self))]
2019-08-10 20:13:12 +02:00
fn lower_use_tree(
&mut self,
tree: &UseTree,
prefix: &Path,
id: NodeId,
2022-02-13 11:30:48 +01:00
vis_span: Span,
2019-08-10 20:13:12 +02:00
ident: &mut Ident,
2021-01-24 17:14:17 +01:00
attrs: Option<&'hir [Attribute]>,
2019-11-28 19:28:50 +01:00
) -> hir::ItemKind<'hir> {
2019-08-10 20:13:12 +02:00
let path = &tree.prefix;
let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect();
match tree.kind {
UseTreeKind::Simple(rename) => {
2019-08-10 20:13:12 +02:00
*ident = tree.ident();
// First, apply the prefix to the path.
2020-08-21 18:51:23 -04:00
let mut path = Path { segments, span: path.span, tokens: None };
2019-08-10 20:13:12 +02:00
// Correctly resolve `self` imports.
if path.segments.len() > 1
&& path.segments.last().unwrap().ident.name == kw::SelfLower
{
let _ = path.segments.pop();
if rename.is_none() {
*ident = path.segments.last().unwrap().ident;
}
}
let res = self.lower_import_res(id, path.span);
let path = self.lower_use_path(res, &path, ParamMode::Explicit);
2019-08-10 20:13:12 +02:00
hir::ItemKind::Use(path, hir::UseKind::Single)
}
UseTreeKind::Glob => {
let res = self.expect_full_res(id);
let res = smallvec![self.lower_res(res)];
let path = Path { segments, span: path.span, tokens: None };
let path = self.lower_use_path(res, &path, ParamMode::Explicit);
2019-08-10 20:13:12 +02:00
hir::ItemKind::Use(path, hir::UseKind::Glob)
}
UseTreeKind::Nested { items: ref trees, .. } => {
2019-08-10 20:13:12 +02:00
// Nested imports are desugared into simple imports.
// So, if we start with
//
// ```
// pub(x) use foo::{a, b};
// ```
//
// we will create three items:
//
// ```
// pub(x) use foo::a;
// pub(x) use foo::b;
// pub(x) use foo::{}; // <-- this is called the `ListStem`
// ```
//
// The first two are produced by recursively invoking
// `lower_use_tree` (and indeed there may be things
// like `use foo::{a::{b, c}}` and so forth). They
2019-08-10 20:13:12 +02:00
// wind up being directly added to
// `self.items`. However, the structure of this
// function also requires us to return one item, and
// for that we return the `{}` import (called the
// `ListStem`).
let span = prefix.span.to(path.span);
let prefix = Path { segments, span, tokens: None };
2019-08-10 20:13:12 +02:00
// Add all the nested `PathListItem`s to the HIR.
for &(ref use_tree, id) in trees {
2021-07-18 20:09:20 +02:00
let new_hir_id = self.local_def_id(id);
2019-08-10 20:13:12 +02:00
// Each `use` import is an item and thus are owners of the
// names in the path. Up to this point the nested import is
// the current owner, since we want each desugared import to
// own its own names, we have to adjust the owner before
// lowering the rest of the import.
self.with_hir_id_owner(id, |this| {
let mut ident = *ident;
// `prefix` is lowered multiple times, but in different HIR owners.
// So each segment gets renewed `HirId` with the same
// `ItemLocalId` and the new owner. (See `lower_node_id`)
2019-09-26 17:51:36 +01:00
let kind =
2022-02-13 11:30:48 +01:00
this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs);
2021-01-24 17:14:17 +01:00
if let Some(attrs) = attrs {
this.attrs.insert(hir::ItemLocalId::ZERO, attrs);
2021-01-24 17:14:17 +01:00
}
2019-12-22 17:42:04 -05:00
let item = hir::Item {
owner_id: hir::OwnerId { def_id: new_hir_id },
ident: this.lower_ident(ident),
2019-09-26 17:51:36 +01:00
kind,
2022-02-13 11:30:48 +01:00
vis_span,
span: this.lower_span(use_tree.span),
};
hir::OwnerNode::Item(this.arena.alloc(item))
2019-08-10 20:13:12 +02:00
});
}
// Condition should match `build_reduced_graph_for_use_tree`.
let path = if trees.is_empty()
&& !(prefix.segments.is_empty()
|| prefix.segments.len() == 1
&& prefix.segments[0].ident.name == kw::PathRoot)
{
// For empty lists we need to lower the prefix so it is checked for things
// like stability later.
let res = self.lower_import_res(id, span);
self.lower_use_path(res, &prefix, ParamMode::Explicit)
} else {
// For non-empty lists we can just drop all the data, the prefix is already
// present in HIR as a part of nested imports.
self.arena.alloc(hir::UsePath { res: smallvec![], segments: &[], span })
};
2019-08-10 20:13:12 +02:00
hir::ItemKind::Use(path, hir::UseKind::ListStem)
}
}
}
fn lower_assoc_item(
&mut self,
item: &AssocItem,
ctxt: AssocCtxt,
parent_hir: &'hir hir::OwnerInfo<'hir>,
) -> hir::OwnerNode<'hir> {
// Evaluate with the lifetimes in `params` in-scope.
// This is used to track which lifetimes have already been defined,
// and which need to be replicated when lowering an async fn.
2024-06-14 12:16:15 +00:00
let parent_item = parent_hir.node().expect_item();
let constness = match parent_item.kind {
hir::ItemKind::Impl(impl_) => {
self.is_in_trait_impl = impl_.of_trait.is_some();
2024-06-14 12:16:15 +00:00
// N.B. the impl should always lower to methods that have `const host: bool` params if the trait
// is const. It doesn't matter whether the `impl` itself is const. Disallowing const fn from
// calling non-const impls are done through associated types.
if let Some(def_id) = impl_.of_trait.and_then(|tr| tr.trait_def_id()) {
if let Some(local_def) = def_id.as_local() {
match &self.ast_index[local_def] {
AstOwner::Item(ast::Item { attrs, .. }) => attrs
.iter()
.find(|attr| attr.has_name(sym::const_trait))
.map_or(Const::No, |attr| Const::Yes(attr.span)),
_ => Const::No,
}
} else {
2024-07-01 08:36:28 +00:00
if self.tcx.is_const_trait(def_id) {
// FIXME(effects) span
Const::Yes(self.tcx.def_ident_span(def_id).unwrap())
} else {
Const::No
}
2024-06-14 12:16:15 +00:00
}
} else {
Const::No
}
}
2024-06-14 12:16:15 +00:00
hir::ItemKind::Trait(_, _, _, _, _) => parent_hir
.attrs
.get(parent_item.hir_id().local_id)
.iter()
.find(|attr| attr.has_name(sym::const_trait))
.map_or(Const::No, |attr| Const::Yes(attr.span)),
kind => {
span_bug!(item.span, "assoc item has unexpected kind of parent: {}", kind.descr())
}
};
match ctxt {
2024-06-14 12:16:15 +00:00
AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item, constness)),
AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item, constness)),
}
}
fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> {
let hir_id = self.lower_node_id(i.id);
let owner_id = hir_id.expect_owner();
2020-11-27 00:35:22 +01:00
self.lower_attrs(hir_id, &i.attrs);
let item = hir::ForeignItem {
owner_id,
ident: self.lower_ident(i.ident),
kind: match &i.kind {
ForeignItemKind::Fn(box Fn { sig, generics, .. }) => {
let fdec = &sig.decl;
let itctx = ImplTraitContext::Universal;
2022-04-07 20:54:13 +02:00
let (generics, (fn_dec, fn_args)) =
2024-06-14 12:16:15 +00:00
self.lower_generics(generics, Const::No, false, i.id, itctx, |this| {
2019-08-10 20:13:12 +02:00
(
// Disallow `impl Trait` in foreign items.
2022-09-02 15:57:31 +00:00
this.lower_fn_decl(
fdec,
i.id,
2022-09-02 15:57:31 +00:00
sig.span,
FnDeclKind::ExternFn,
2023-11-30 16:39:56 -08:00
None,
2022-09-02 15:57:31 +00:00
),
this.lower_fn_params_to_names(fdec),
2019-08-10 20:13:12 +02:00
)
2022-04-07 20:54:13 +02:00
});
let safety = self.lower_safety(sig.header.safety, hir::Safety::Unsafe);
2019-08-10 20:13:12 +02:00
hir::ForeignItemKind::Fn(fn_dec, fn_args, generics, safety)
2019-08-10 20:13:12 +02:00
}
ForeignItemKind::Static(box StaticItem { ty, mutability, expr: _, safety }) => {
let ty = self
.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy));
let safety = self.lower_safety(*safety, hir::Safety::Unsafe);
hir::ForeignItemKind::Static(ty, *mutability, safety)
2019-08-10 20:13:12 +02:00
}
ForeignItemKind::TyAlias(..) => hir::ForeignItemKind::Type,
2020-02-29 19:32:20 +03:00
ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"),
2019-08-10 20:13:12 +02:00
},
2022-02-13 10:29:46 +01:00
vis_span: self.lower_span(i.vis.span),
span: self.lower_span(i.span),
};
self.arena.alloc(item)
2019-08-10 20:13:12 +02:00
}
2021-07-15 22:19:39 +02:00
fn lower_foreign_item_ref(&mut self, i: &ForeignItem) -> hir::ForeignItemRef {
2020-11-11 21:57:54 +01:00
hir::ForeignItemRef {
id: hir::ForeignItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } },
ident: self.lower_ident(i.ident),
span: self.lower_span(i.span),
2020-11-11 21:57:54 +01:00
}
}
2019-11-29 09:26:18 +01:00
fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> {
let hir_id = self.lower_node_id(v.id);
self.lower_attrs(hir_id, &v.attrs);
2019-08-13 21:40:21 -03:00
hir::Variant {
hir_id,
def_id: self.local_def_id(v.id),
data: self.lower_variant_data(hir_id, &v.data),
disr_expr: v.disr_expr.as_ref().map(|e| self.lower_anon_const_to_anon_const(e)),
ident: self.lower_ident(v.ident),
span: self.lower_span(v.span),
2019-08-10 20:13:12 +02:00
}
}
fn lower_variant_data(
&mut self,
parent_id: hir::HirId,
vdata: &VariantData,
) -> hir::VariantData<'hir> {
match vdata {
VariantData::Struct { fields, recovered } => hir::VariantData::Struct {
fields: self
.arena
.alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f))),
recovered: *recovered,
},
VariantData::Tuple(fields, id) => {
let ctor_id = self.lower_node_id(*id);
2021-01-24 17:14:17 +01:00
self.alias_attrs(ctor_id, parent_id);
hir::VariantData::Tuple(
self.arena.alloc_from_iter(
fields.iter().enumerate().map(|f| self.lower_field_def(f)),
),
ctor_id,
self.local_def_id(*id),
)
}
VariantData::Unit(id) => {
let ctor_id = self.lower_node_id(*id);
2021-01-24 17:14:17 +01:00
self.alias_attrs(ctor_id, parent_id);
hir::VariantData::Unit(ctor_id, self.local_def_id(*id))
}
2019-08-10 20:13:12 +02:00
}
}
pub(super) fn lower_field_def(
&mut self,
(index, f): (usize, &FieldDef),
) -> hir::FieldDef<'hir> {
let ty = if let TyKind::Path(qself, path) = &f.ty.kind {
2019-08-10 20:13:12 +02:00
let t = self.lower_path_ty(
&f.ty,
qself,
path,
ParamMode::ExplicitNamed, // no `'_` in declarations (Issue #61124)
ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy),
2019-08-10 20:13:12 +02:00
);
2019-11-29 09:40:33 +01:00
self.arena.alloc(t)
2019-08-10 20:13:12 +02:00
} else {
self.lower_ty(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy))
2019-08-10 20:13:12 +02:00
};
let hir_id = self.lower_node_id(f.id);
2020-11-27 00:27:34 +01:00
self.lower_attrs(hir_id, &f.attrs);
hir::FieldDef {
span: self.lower_span(f.span),
hir_id,
def_id: self.local_def_id(f.id),
2019-08-10 20:13:12 +02:00
ident: match f.ident {
Some(ident) => self.lower_ident(ident),
2019-08-10 20:13:12 +02:00
// FIXME(jseyfried): positional field hygiene.
None => Ident::new(sym::integer(index), self.lower_span(f.span)),
2019-08-10 20:13:12 +02:00
},
2022-02-13 01:54:13 +01:00
vis_span: self.lower_span(f.vis.span),
2019-08-10 20:13:12 +02:00
ty,
}
}
2024-06-14 12:16:15 +00:00
fn lower_trait_item(
&mut self,
i: &AssocItem,
trait_constness: Const,
) -> &'hir hir::TraitItem<'hir> {
let hir_id = self.lower_node_id(i.id);
self.lower_attrs(hir_id, &i.attrs);
let trait_item_def_id = hir_id.expect_owner();
2019-08-10 20:13:12 +02:00
let (generics, kind, has_default) = match &i.kind {
2023-05-04 16:40:57 +02:00
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => {
let (generics, kind) = self.lower_generics(
generics,
2023-05-04 16:40:57 +02:00
Const::No,
2024-06-14 12:16:15 +00:00
false,
2023-05-04 16:40:57 +02:00
i.id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
2023-05-04 16:40:57 +02:00
|this| {
let ty = this
.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
2023-05-04 16:40:57 +02:00
let body = expr.as_ref().map(|x| this.lower_const_body(i.span, Some(x)));
hir::TraitItemKind::Const(ty, body)
},
);
(generics, kind, expr.is_some())
2019-12-01 12:49:54 +01:00
}
AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => {
let names = self.lower_fn_params_to_names(&sig.decl);
let (generics, sig) = self.lower_method_sig(
generics,
sig,
i.id,
FnDeclKind::Trait,
2023-12-05 21:39:36 +00:00
sig.header.coroutine_kind,
2024-06-14 12:16:15 +00:00
trait_constness,
);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
2019-08-10 20:13:12 +02:00
}
AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => {
let body_id = self.lower_maybe_coroutine_body(
2024-06-27 14:56:57 -04:00
sig.span,
i.span,
hir_id,
&sig.decl,
2023-12-05 21:39:36 +00:00
sig.header.coroutine_kind,
Some(body),
);
let (generics, sig) = self.lower_method_sig(
generics,
sig,
2022-04-07 20:54:13 +02:00
i.id,
FnDeclKind::Trait,
2023-12-05 21:39:36 +00:00
sig.header.coroutine_kind,
2024-06-14 12:16:15 +00:00
trait_constness,
);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
2019-08-10 20:13:12 +02:00
}
AssocItemKind::Type(box TyAlias { generics, where_clauses, bounds, ty, .. }) => {
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
let (generics, kind) = self.lower_generics(
&generics,
Const::No,
2024-06-14 12:16:15 +00:00
false,
i.id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
let ty = ty.as_ref().map(|x| {
this.lower_ty(
x,
ImplTraitContext::Disallowed(ImplTraitPosition::AssocTy),
)
});
hir::TraitItemKind::Type(
this.lower_param_bounds(
bounds,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
),
ty,
)
},
);
(generics, kind, ty.is_some())
2019-08-10 20:13:12 +02:00
}
2023-11-26 15:57:31 +03:00
AssocItemKind::Delegation(box delegation) => {
let delegation_results = self.lower_delegation(delegation, i.id);
let item_kind = hir::TraitItemKind::Fn(
delegation_results.sig,
hir::TraitFn::Provided(delegation_results.body_id),
);
(delegation_results.generics, item_kind, true)
}
AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
panic!("macros should have been expanded by now")
}
2019-08-10 20:13:12 +02:00
};
let item = hir::TraitItem {
owner_id: trait_item_def_id,
ident: self.lower_ident(i.ident),
generics,
kind,
span: self.lower_span(i.span),
defaultness: hir::Defaultness::Default { has_value: has_default },
};
self.arena.alloc(item)
2019-08-10 20:13:12 +02:00
}
fn lower_trait_item_ref(&mut self, i: &AssocItem) -> hir::TraitItemRef {
let kind = match &i.kind {
AssocItemKind::Const(..) => hir::AssocItemKind::Const,
AssocItemKind::Type(..) => hir::AssocItemKind::Type,
AssocItemKind::Fn(box Fn { sig, .. }) => {
hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }
2019-08-10 20:13:12 +02:00
}
2023-11-26 15:57:31 +03:00
AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn {
has_self: self.delegation_has_self(i.id, delegation.id, i.span),
},
AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
panic!("macros should have been expanded by now")
}
2019-08-10 20:13:12 +02:00
};
let id = hir::TraitItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } };
hir::TraitItemRef {
id,
ident: self.lower_ident(i.ident),
span: self.lower_span(i.span),
kind,
}
2019-08-10 20:13:12 +02:00
}
/// Construct `ExprKind::Err` for the given `span`.
pub(crate) fn expr_err(&mut self, span: Span, guar: ErrorGuaranteed) -> hir::Expr<'hir> {
self.expr(span, hir::ExprKind::Err(guar))
}
2024-06-14 12:16:15 +00:00
fn lower_impl_item(
&mut self,
i: &AssocItem,
constness_of_trait: Const,
) -> &'hir hir::ImplItem<'hir> {
// Since `default impl` is not yet implemented, this is always true in impls.
let has_value = true;
let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
let hir_id = self.lower_node_id(i.id);
self.lower_attrs(hir_id, &i.attrs);
let (generics, kind) = match &i.kind {
2023-05-04 16:40:57 +02:00
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
generics,
2023-05-04 16:40:57 +02:00
Const::No,
2024-06-14 12:16:15 +00:00
false,
2023-05-04 16:40:57 +02:00
i.id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
2023-05-04 16:40:57 +02:00
|this| {
let ty =
this.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
2023-05-04 16:40:57 +02:00
let body = this.lower_const_body(i.span, expr.as_deref());
hir::ImplItemKind::Const(ty, body)
},
),
AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => {
let body_id = self.lower_maybe_coroutine_body(
2024-06-27 14:56:57 -04:00
sig.span,
i.span,
hir_id,
&sig.decl,
2023-12-05 21:39:36 +00:00
sig.header.coroutine_kind,
body.as_deref(),
);
2019-08-10 20:13:12 +02:00
let (generics, sig) = self.lower_method_sig(
2020-02-13 18:03:38 +01:00
generics,
2019-08-10 20:13:12 +02:00
sig,
2022-04-07 20:54:13 +02:00
i.id,
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
2023-12-05 21:39:36 +00:00
sig.header.coroutine_kind,
2024-06-14 12:16:15 +00:00
constness_of_trait,
2019-08-10 20:13:12 +02:00
);
2020-03-05 09:57:34 -06:00
(generics, hir::ImplItemKind::Fn(sig, body_id))
2019-08-10 20:13:12 +02:00
}
AssocItemKind::Type(box TyAlias { generics, where_clauses, ty, .. }) => {
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
self.lower_generics(
&generics,
Const::No,
2024-06-14 12:16:15 +00:00
false,
i.id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| match ty {
None => {
let guar = this.dcx().span_delayed_bug(
2023-02-22 21:19:42 +00:00
i.span,
"expected to lower associated type, but it was missing",
);
let ty = this.arena.alloc(this.ty(i.span, hir::TyKind::Err(guar)));
hir::ImplItemKind::Type(ty)
}
Some(ty) => {
let ty = this.lower_ty(
ty,
2024-03-06 18:44:55 +00:00
ImplTraitContext::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias {
parent: this.local_def_id(i.id),
in_assoc_ty: true,
},
2024-03-06 18:44:55 +00:00
fn_kind: None,
},
);
hir::ImplItemKind::Type(ty)
}
},
)
}
2023-11-26 15:57:31 +03:00
AssocItemKind::Delegation(box delegation) => {
let delegation_results = self.lower_delegation(delegation, i.id);
(
delegation_results.generics,
hir::ImplItemKind::Fn(delegation_results.sig, delegation_results.body_id),
)
}
AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
panic!("macros should have been expanded by now")
}
2019-08-10 20:13:12 +02:00
};
let item = hir::ImplItem {
owner_id: hir_id.expect_owner(),
ident: self.lower_ident(i.ident),
2019-08-10 20:13:12 +02:00
generics,
kind,
2022-02-13 10:54:07 +01:00
vis_span: self.lower_span(i.vis.span),
span: self.lower_span(i.span),
defaultness,
};
self.arena.alloc(item)
2019-08-10 20:13:12 +02:00
}
2021-07-15 22:19:39 +02:00
fn lower_impl_item_ref(&mut self, i: &AssocItem) -> hir::ImplItemRef {
2019-08-10 20:13:12 +02:00
hir::ImplItemRef {
id: hir::ImplItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } },
ident: self.lower_ident(i.ident),
span: self.lower_span(i.span),
kind: match &i.kind {
AssocItemKind::Const(..) => hir::AssocItemKind::Const,
AssocItemKind::Type(..) => hir::AssocItemKind::Type,
AssocItemKind::Fn(box Fn { sig, .. }) => {
hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }
2019-08-10 20:13:12 +02:00
}
2023-11-26 15:57:31 +03:00
AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn {
has_self: self.delegation_has_self(i.id, delegation.id, i.span),
},
AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
panic!("macros should have been expanded by now")
}
2019-08-10 20:13:12 +02:00
},
trait_item_def_id: self
.resolver
.get_partial_res(i.id)
.map(|r| r.expect_full_res().opt_def_id())
.unwrap_or(None),
2019-08-10 20:13:12 +02:00
}
}
fn lower_defaultness(
&self,
d: Defaultness,
has_value: bool,
) -> (hir::Defaultness, Option<Span>) {
2019-08-10 20:13:12 +02:00
match d {
Defaultness::Default(sp) => {
(hir::Defaultness::Default { has_value }, Some(self.lower_span(sp)))
}
2019-08-10 20:13:12 +02:00
Defaultness::Final => {
assert!(has_value);
(hir::Defaultness::Final, None)
2019-08-10 20:13:12 +02:00
}
}
}
2019-11-29 13:43:03 +01:00
fn record_body(
&mut self,
params: &'hir [hir::Param<'hir>],
value: hir::Expr<'hir>,
) -> hir::BodyId {
let body = hir::Body { params, value: self.arena.alloc(value) };
2019-08-10 20:13:12 +02:00
let id = body.id();
2021-07-16 14:42:26 +02:00
debug_assert_eq!(id.hir_id.owner, self.current_hir_id_owner);
2021-10-21 23:08:57 +02:00
self.bodies.push((id.hir_id.local_id, self.arena.alloc(body)));
2019-08-10 20:13:12 +02:00
id
}
pub(super) fn lower_body(
2019-08-10 20:13:12 +02:00
&mut self,
2019-11-29 13:43:03 +01:00
f: impl FnOnce(&mut Self) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>),
2019-08-10 20:13:12 +02:00
) -> hir::BodyId {
2023-10-30 23:35:35 +00:00
let prev_coroutine_kind = self.coroutine_kind.take();
2020-04-01 00:41:40 +02:00
let task_context = self.task_context.take();
let (parameters, result) = f(self);
let body_id = self.record_body(parameters, result);
2020-04-01 00:41:40 +02:00
self.task_context = task_context;
2023-10-30 23:35:35 +00:00
self.coroutine_kind = prev_coroutine_kind;
2019-08-10 20:13:12 +02:00
body_id
}
2019-11-29 13:43:03 +01:00
fn lower_param(&mut self, param: &Param) -> hir::Param<'hir> {
let hir_id = self.lower_node_id(param.id);
2020-11-26 23:51:27 +01:00
self.lower_attrs(hir_id, &param.attrs);
hir::Param {
hir_id,
pat: self.lower_pat(&param.pat),
ty_span: self.lower_span(param.ty.span),
span: self.lower_span(param.span),
2019-08-10 20:23:34 +02:00
}
}
2019-08-10 20:13:12 +02:00
pub(super) fn lower_fn_body(
&mut self,
decl: &FnDecl,
2019-11-29 13:43:03 +01:00
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
2019-08-10 20:13:12 +02:00
) -> hir::BodyId {
self.lower_body(|this| {
(
2019-11-29 11:09:23 +01:00
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))),
2019-08-10 20:13:12 +02:00
body(this),
)
2019-12-22 17:42:04 -05:00
})
2019-08-10 20:13:12 +02:00
}
fn lower_fn_body_block(
&mut self,
span: Span,
decl: &FnDecl,
body: Option<&Block>,
) -> hir::BodyId {
self.lower_fn_body(decl, |this| this.lower_block_expr_opt(span, body))
}
2019-11-29 13:43:03 +01:00
fn lower_block_expr_opt(&mut self, span: Span, block: Option<&Block>) -> hir::Expr<'hir> {
match block {
Some(block) => self.lower_block_expr(block),
None => self.expr_err(span, self.dcx().has_errors().unwrap()),
}
2019-08-10 20:13:12 +02:00
}
pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId {
2019-11-29 11:09:23 +01:00
self.lower_body(|this| {
(
&[],
match expr {
2019-12-01 21:10:43 +01:00
Some(expr) => this.lower_expr_mut(expr),
None => this.expr_err(span, this.dcx().span_delayed_bug(span, "no block")),
2019-12-22 17:42:04 -05:00
},
)
})
2019-08-10 20:13:12 +02:00
}
/// Takes what may be the body of an `async fn` or a `gen fn` and wraps it in an `async {}` or
/// `gen {}` block as appropriate.
fn lower_maybe_coroutine_body(
2019-08-10 20:13:12 +02:00
&mut self,
2024-06-27 14:56:57 -04:00
fn_decl_span: Span,
span: Span,
fn_id: hir::HirId,
2019-08-10 20:13:12 +02:00
decl: &FnDecl,
2023-12-05 21:39:36 +00:00
coroutine_kind: Option<CoroutineKind>,
body: Option<&Block>,
2019-08-10 20:13:12 +02:00
) -> hir::BodyId {
2023-12-05 21:39:36 +00:00
let (Some(coroutine_kind), Some(body)) = (coroutine_kind, body) else {
2023-11-30 16:39:56 -08:00
return self.lower_fn_body_block(span, decl, body);
};
2019-08-10 20:13:12 +02:00
self.lower_body(|this| {
let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
decl,
|this| this.lower_block_expr(body),
2024-06-27 14:56:57 -04:00
fn_decl_span,
body.span,
coroutine_kind,
hir::CoroutineSource::Fn,
);
2019-08-10 20:13:12 +02:00
// FIXME(async_fn_track_caller): Can this be moved above?
let hir_id = this.lower_node_id(coroutine_kind.closure_id());
this.maybe_forward_track_caller(body.span, fn_id, hir_id);
2019-08-10 20:13:12 +02:00
(parameters, expr)
})
}
2019-08-10 20:13:12 +02:00
/// Lowers a desugared coroutine body after moving all of the arguments
/// into the body. This is to make sure that the future actually owns the
/// arguments that are passed to the function, and to ensure things like
/// drop order are stable.
pub fn lower_coroutine_body_with_moved_arguments(
&mut self,
decl: &FnDecl,
lower_body: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::Expr<'hir>,
2024-06-27 14:56:57 -04:00
fn_decl_span: Span,
body_span: Span,
coroutine_kind: CoroutineKind,
coroutine_source: hir::CoroutineSource,
) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>) {
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
let mut statements: Vec<hir::Stmt<'_>> = Vec::new();
// Async function parameters are lowered into the closure body so that they are
// captured and so that the drop order matches the equivalent non-async functions.
//
// from:
//
// async fn foo(<pattern>: <ty>, <pattern>: <ty>, <pattern>: <ty>) {
// <body>
// }
//
// into:
//
// fn foo(__arg0: <ty>, __arg1: <ty>, __arg2: <ty>) {
// async move {
// let __arg2 = __arg2;
// let <pattern> = __arg2;
// let __arg1 = __arg1;
// let <pattern> = __arg1;
// let __arg0 = __arg0;
// let <pattern> = __arg0;
// drop-temps { <body> } // see comments later in fn for details
// }
// }
//
// If `<pattern>` is a simple ident, then it is lowered to a single
// `let <pattern> = <pattern>;` statement as an optimization.
//
// Note that the body is embedded in `drop-temps`; an
// equivalent desugaring would be `return { <body>
// };`. The key point is that we wish to drop all the
// let-bound variables and temporaries created in the body
// (and its tail expression!) before we drop the
// parameters (c.f. rust-lang/rust#64512).
for (index, parameter) in decl.inputs.iter().enumerate() {
let parameter = self.lower_param(parameter);
let span = parameter.pat.span;
// Check if this is a binding pattern, if so, we can optimize and avoid adding a
// `let <pat> = __argN;` statement. In this case, we do not rename the parameter.
let (ident, is_simple_parameter) = match parameter.pat.kind {
hir::PatKind::Binding(hir::BindingMode(ByRef::No, _), _, ident, _) => (ident, true),
// For `ref mut` or wildcard arguments, we can't reuse the binding, but
// we can keep the same name for the parameter.
// This lets rustdoc render it correctly in documentation.
hir::PatKind::Binding(_, _, ident, _) => (ident, false),
hir::PatKind::Wild => {
(Ident::with_dummy_span(rustc_span::symbol::kw::Underscore), false)
}
_ => {
// Replace the ident for bindings that aren't simple.
let name = format!("__arg{index}");
let ident = Ident::from_str(&name);
2019-08-10 20:13:12 +02:00
(ident, false)
}
};
2019-08-10 20:13:12 +02:00
let desugared_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
// Construct a parameter representing `__argN: <ty>` to replace the parameter of the
// async function.
//
// If this is the simple case, this parameter will end up being the same as the
// original parameter, but with a different pattern id.
let stmt_attrs = self.attrs.get(&parameter.hir_id.local_id).copied();
let (new_parameter_pat, new_parameter_id) = self.pat_ident(desugared_span, ident);
let new_parameter = hir::Param {
hir_id: parameter.hir_id,
pat: new_parameter_pat,
ty_span: self.lower_span(parameter.ty_span),
span: self.lower_span(parameter.span),
};
if is_simple_parameter {
// If this is the simple case, then we only insert one statement that is
// `let <pat> = <pat>;`. We re-use the original argument's pattern so that
// `HirId`s are densely assigned.
let expr = self.expr_ident(desugared_span, ident, new_parameter_id);
let stmt = self.stmt_let_pat(
stmt_attrs,
desugared_span,
Some(expr),
parameter.pat,
hir::LocalSource::AsyncFn,
);
statements.push(stmt);
} else {
// If this is not the simple case, then we construct two statements:
//
// ```
// let __argN = __argN;
// let <pat> = __argN;
// ```
//
// The first statement moves the parameter into the closure and thus ensures
// that the drop order is correct.
//
// The second statement creates the bindings that the user wrote.
// Construct the `let mut __argN = __argN;` statement. It must be a mut binding
// because the user may have specified a `ref mut` binding in the next
// statement.
let (move_pat, move_id) =
self.pat_ident_binding_mode(desugared_span, ident, hir::BindingMode::MUT);
let move_expr = self.expr_ident(desugared_span, ident, new_parameter_id);
let move_stmt = self.stmt_let_pat(
None,
desugared_span,
Some(move_expr),
move_pat,
hir::LocalSource::AsyncFn,
);
2019-11-29 19:01:31 +01:00
// Construct the `let <pat> = __argN;` statement. We re-use the original
// parameter's pattern so that `HirId`s are densely assigned.
let pattern_expr = self.expr_ident(desugared_span, ident, move_id);
let pattern_stmt = self.stmt_let_pat(
stmt_attrs,
desugared_span,
Some(pattern_expr),
parameter.pat,
hir::LocalSource::AsyncFn,
);
statements.push(move_stmt);
statements.push(pattern_stmt);
};
parameters.push(new_parameter);
}
let mkbody = |this: &mut LoweringContext<'_, 'hir>| {
// Create a block from the user's function body:
let user_body = lower_body(this);
// Transform into `drop-temps { <user-body> }`, an expression:
let desugared_span =
this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None);
let user_body = this.expr_drop_temps(desugared_span, this.arena.alloc(user_body));
// As noted above, create the final block like
//
// ```
// {
// let $param_pattern = $raw_param;
// ...
// drop-temps { <user-body> }
// }
// ```
let body = this.block_all(
desugared_span,
this.arena.alloc_from_iter(statements),
Some(user_body),
);
2019-11-29 11:09:23 +01:00
this.expr_block(body)
};
let desugaring_kind = match coroutine_kind {
CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async,
CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen,
CoroutineKind::AsyncGen { .. } => hir::CoroutineDesugaring::AsyncGen,
};
let closure_id = coroutine_kind.closure_id();
let coroutine_expr = self.make_desugared_coroutine_expr(
// The default capture mode here is by-ref. Later on during upvar analysis,
// we will force the captured arguments to by-move, but for async closures,
// we want to make sure that we avoid unnecessarily moving captures, or else
// all async closures would default to `FnOnce` as their calling mode.
CaptureBy::Ref,
closure_id,
None,
2024-06-27 14:56:57 -04:00
fn_decl_span,
body_span,
desugaring_kind,
coroutine_source,
mkbody,
);
let expr = hir::Expr {
hir_id: self.lower_node_id(closure_id),
kind: coroutine_expr,
span: self.lower_span(body_span),
};
(self.arena.alloc_from_iter(parameters), expr)
2019-08-10 20:13:12 +02:00
}
fn lower_method_sig(
&mut self,
generics: &Generics,
2019-11-07 13:11:59 +01:00
sig: &FnSig,
2022-04-07 20:54:13 +02:00
id: NodeId,
kind: FnDeclKind,
2023-12-05 21:39:36 +00:00
coroutine_kind: Option<CoroutineKind>,
2024-06-14 12:16:15 +00:00
parent_constness: Const,
2022-02-05 15:26:49 +01:00
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
2021-07-08 21:58:05 +02:00
let header = self.lower_fn_header(sig.header);
// Don't pass along the user-provided constness of trait associated functions; we don't want to
// synthesize a host effect param for them. We reject `const` on them during AST validation.
2024-06-14 12:16:15 +00:00
let constness =
if kind == FnDeclKind::Inherent { sig.header.constness } else { parent_constness };
let itctx = ImplTraitContext::Universal;
2024-06-14 12:16:15 +00:00
let (generics, decl) =
self.lower_generics(generics, constness, kind == FnDeclKind::Impl, id, itctx, |this| {
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
});
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
2019-08-10 20:13:12 +02:00
}
pub(super) fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
2023-12-05 21:39:36 +00:00
let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind {
2023-11-30 14:54:39 -08:00
hir::IsAsync::Async(span)
} else {
hir::IsAsync::NotAsync
};
2019-08-10 20:13:12 +02:00
hir::FnHeader {
safety: self.lower_safety(h.safety, hir::Safety::Safe),
2023-11-30 14:54:39 -08:00
asyncness: asyncness,
constness: self.lower_constness(h.constness),
2021-07-08 21:58:05 +02:00
abi: self.lower_extern(h.ext),
2019-08-10 20:13:12 +02:00
}
}
pub(super) fn lower_abi(&mut self, abi: StrLit) -> abi::Abi {
feat: `riscv-interrupt-{m,s}` calling conventions Similar to prior support added for the mips430, avr, and x86 targets this change implements the rough equivalent of clang's [`__attribute__((interrupt))`][clang-attr] for riscv targets, enabling e.g. ```rust static mut CNT: usize = 0; pub extern "riscv-interrupt-m" fn isr_m() { unsafe { CNT += 1; } } ``` to produce highly effective assembly like: ```asm pub extern "riscv-interrupt-m" fn isr_m() { 420003a0: 1141 addi sp,sp,-16 unsafe { CNT += 1; 420003a2: c62a sw a0,12(sp) 420003a4: c42e sw a1,8(sp) 420003a6: 3fc80537 lui a0,0x3fc80 420003aa: 63c52583 lw a1,1596(a0) # 3fc8063c <_ZN12esp_riscv_rt3CNT17hcec3e3a214887d53E.0> 420003ae: 0585 addi a1,a1,1 420003b0: 62b52e23 sw a1,1596(a0) } } 420003b4: 4532 lw a0,12(sp) 420003b6: 45a2 lw a1,8(sp) 420003b8: 0141 addi sp,sp,16 420003ba: 30200073 mret ``` (disassembly via `riscv64-unknown-elf-objdump -C -S --disassemble ./esp32c3-hal/target/riscv32imc-unknown-none-elf/release/examples/gpio_interrupt`) This outcome is superior to hand-coded interrupt routines which, lacking visibility into any non-assembly body of the interrupt handler, have to be very conservative and save the [entire CPU state to the stack frame][full-frame-save]. By instead asking LLVM to only save the registers that it uses, we defer the decision to the tool with the best context: it can more accurately account for the cost of spills if it knows that every additional register used is already at the cost of an implicit spill. At the LLVM level, this is apparently [implemented by] marking every register as "[callee-save]," matching the semantics of an interrupt handler nicely (it has to leave the CPU state just as it found it after its `{m|s}ret`). This approach is not suitable for every interrupt handler, as it makes no attempt to e.g. save the state in a user-accessible stack frame. For a full discussion of those challenges and tradeoffs, please refer to [the interrupt calling conventions RFC][rfc]. Inside rustc, this implementation differs from prior art because LLVM does not expose the "all-saved" function flavor as a calling convention directly, instead preferring to use an attribute that allows for differentiating between "machine-mode" and "superivsor-mode" interrupts. Finally, some effort has been made to guide those who may not yet be aware of the differences between machine-mode and supervisor-mode interrupts as to why no `riscv-interrupt` calling convention is exposed through rustc, and similarly for why `riscv-interrupt-u` makes no appearance (as it would complicate future LLVM upgrades). [clang-attr]: https://clang.llvm.org/docs/AttributeReference.html#interrupt-risc-v [full-frame-save]: https://github.com/esp-rs/esp-riscv-rt/blob/9281af2ecffe13e40992917316f36920c26acaf3/src/lib.rs#L440-L469 [implemented by]: https://github.com/llvm/llvm-project/blob/b7fb2a3fec7c187d58a6d338ab512d9173bca987/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp#L61-L67 [callee-save]: https://github.com/llvm/llvm-project/blob/973f1fe7a8591c7af148e573491ab68cc15b6ecf/llvm/lib/Target/RISCV/RISCVCallingConv.td#L30-L37 [rfc]: https://github.com/rust-lang/rfcs/pull/3246
2023-05-23 15:08:23 -07:00
abi::lookup(abi.symbol_unescaped.as_str()).unwrap_or_else(|err| {
self.error_on_invalid_abi(abi, err);
abi::Abi::Rust
})
}
2021-07-08 21:58:05 +02:00
pub(super) fn lower_extern(&mut self, ext: Extern) -> abi::Abi {
match ext {
Extern::None => abi::Abi::Rust,
2022-07-02 18:25:55 +01:00
Extern::Implicit(_) => abi::Abi::FALLBACK,
Extern::Explicit(abi, _) => self.lower_abi(abi),
}
}
feat: `riscv-interrupt-{m,s}` calling conventions Similar to prior support added for the mips430, avr, and x86 targets this change implements the rough equivalent of clang's [`__attribute__((interrupt))`][clang-attr] for riscv targets, enabling e.g. ```rust static mut CNT: usize = 0; pub extern "riscv-interrupt-m" fn isr_m() { unsafe { CNT += 1; } } ``` to produce highly effective assembly like: ```asm pub extern "riscv-interrupt-m" fn isr_m() { 420003a0: 1141 addi sp,sp,-16 unsafe { CNT += 1; 420003a2: c62a sw a0,12(sp) 420003a4: c42e sw a1,8(sp) 420003a6: 3fc80537 lui a0,0x3fc80 420003aa: 63c52583 lw a1,1596(a0) # 3fc8063c <_ZN12esp_riscv_rt3CNT17hcec3e3a214887d53E.0> 420003ae: 0585 addi a1,a1,1 420003b0: 62b52e23 sw a1,1596(a0) } } 420003b4: 4532 lw a0,12(sp) 420003b6: 45a2 lw a1,8(sp) 420003b8: 0141 addi sp,sp,16 420003ba: 30200073 mret ``` (disassembly via `riscv64-unknown-elf-objdump -C -S --disassemble ./esp32c3-hal/target/riscv32imc-unknown-none-elf/release/examples/gpio_interrupt`) This outcome is superior to hand-coded interrupt routines which, lacking visibility into any non-assembly body of the interrupt handler, have to be very conservative and save the [entire CPU state to the stack frame][full-frame-save]. By instead asking LLVM to only save the registers that it uses, we defer the decision to the tool with the best context: it can more accurately account for the cost of spills if it knows that every additional register used is already at the cost of an implicit spill. At the LLVM level, this is apparently [implemented by] marking every register as "[callee-save]," matching the semantics of an interrupt handler nicely (it has to leave the CPU state just as it found it after its `{m|s}ret`). This approach is not suitable for every interrupt handler, as it makes no attempt to e.g. save the state in a user-accessible stack frame. For a full discussion of those challenges and tradeoffs, please refer to [the interrupt calling conventions RFC][rfc]. Inside rustc, this implementation differs from prior art because LLVM does not expose the "all-saved" function flavor as a calling convention directly, instead preferring to use an attribute that allows for differentiating between "machine-mode" and "superivsor-mode" interrupts. Finally, some effort has been made to guide those who may not yet be aware of the differences between machine-mode and supervisor-mode interrupts as to why no `riscv-interrupt` calling convention is exposed through rustc, and similarly for why `riscv-interrupt-u` makes no appearance (as it would complicate future LLVM upgrades). [clang-attr]: https://clang.llvm.org/docs/AttributeReference.html#interrupt-risc-v [full-frame-save]: https://github.com/esp-rs/esp-riscv-rt/blob/9281af2ecffe13e40992917316f36920c26acaf3/src/lib.rs#L440-L469 [implemented by]: https://github.com/llvm/llvm-project/blob/b7fb2a3fec7c187d58a6d338ab512d9173bca987/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp#L61-L67 [callee-save]: https://github.com/llvm/llvm-project/blob/973f1fe7a8591c7af148e573491ab68cc15b6ecf/llvm/lib/Target/RISCV/RISCVCallingConv.td#L30-L37 [rfc]: https://github.com/rust-lang/rfcs/pull/3246
2023-05-23 15:08:23 -07:00
fn error_on_invalid_abi(&self, abi: StrLit, err: abi::AbiUnsupported) {
let abi_names = abi::enabled_names(self.tcx.features(), abi.span)
.iter()
.map(|s| Symbol::intern(s))
.collect::<Vec<_>>();
let suggested_name = find_best_match_for_name(&abi_names, abi.symbol_unescaped, None);
self.dcx().emit_err(InvalidAbi {
abi: abi.symbol_unescaped,
span: abi.span,
feat: `riscv-interrupt-{m,s}` calling conventions Similar to prior support added for the mips430, avr, and x86 targets this change implements the rough equivalent of clang's [`__attribute__((interrupt))`][clang-attr] for riscv targets, enabling e.g. ```rust static mut CNT: usize = 0; pub extern "riscv-interrupt-m" fn isr_m() { unsafe { CNT += 1; } } ``` to produce highly effective assembly like: ```asm pub extern "riscv-interrupt-m" fn isr_m() { 420003a0: 1141 addi sp,sp,-16 unsafe { CNT += 1; 420003a2: c62a sw a0,12(sp) 420003a4: c42e sw a1,8(sp) 420003a6: 3fc80537 lui a0,0x3fc80 420003aa: 63c52583 lw a1,1596(a0) # 3fc8063c <_ZN12esp_riscv_rt3CNT17hcec3e3a214887d53E.0> 420003ae: 0585 addi a1,a1,1 420003b0: 62b52e23 sw a1,1596(a0) } } 420003b4: 4532 lw a0,12(sp) 420003b6: 45a2 lw a1,8(sp) 420003b8: 0141 addi sp,sp,16 420003ba: 30200073 mret ``` (disassembly via `riscv64-unknown-elf-objdump -C -S --disassemble ./esp32c3-hal/target/riscv32imc-unknown-none-elf/release/examples/gpio_interrupt`) This outcome is superior to hand-coded interrupt routines which, lacking visibility into any non-assembly body of the interrupt handler, have to be very conservative and save the [entire CPU state to the stack frame][full-frame-save]. By instead asking LLVM to only save the registers that it uses, we defer the decision to the tool with the best context: it can more accurately account for the cost of spills if it knows that every additional register used is already at the cost of an implicit spill. At the LLVM level, this is apparently [implemented by] marking every register as "[callee-save]," matching the semantics of an interrupt handler nicely (it has to leave the CPU state just as it found it after its `{m|s}ret`). This approach is not suitable for every interrupt handler, as it makes no attempt to e.g. save the state in a user-accessible stack frame. For a full discussion of those challenges and tradeoffs, please refer to [the interrupt calling conventions RFC][rfc]. Inside rustc, this implementation differs from prior art because LLVM does not expose the "all-saved" function flavor as a calling convention directly, instead preferring to use an attribute that allows for differentiating between "machine-mode" and "superivsor-mode" interrupts. Finally, some effort has been made to guide those who may not yet be aware of the differences between machine-mode and supervisor-mode interrupts as to why no `riscv-interrupt` calling convention is exposed through rustc, and similarly for why `riscv-interrupt-u` makes no appearance (as it would complicate future LLVM upgrades). [clang-attr]: https://clang.llvm.org/docs/AttributeReference.html#interrupt-risc-v [full-frame-save]: https://github.com/esp-rs/esp-riscv-rt/blob/9281af2ecffe13e40992917316f36920c26acaf3/src/lib.rs#L440-L469 [implemented by]: https://github.com/llvm/llvm-project/blob/b7fb2a3fec7c187d58a6d338ab512d9173bca987/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp#L61-L67 [callee-save]: https://github.com/llvm/llvm-project/blob/973f1fe7a8591c7af148e573491ab68cc15b6ecf/llvm/lib/Target/RISCV/RISCVCallingConv.td#L30-L37 [rfc]: https://github.com/rust-lang/rfcs/pull/3246
2023-05-23 15:08:23 -07:00
explain: match err {
abi::AbiUnsupported::Reason { explain } => Some(InvalidAbiReason(explain)),
_ => None,
},
suggestion: suggested_name.map(|suggested_name| InvalidAbiSuggestion {
span: abi.span,
suggestion: format!("\"{suggested_name}\""),
}),
command: "rustc --print=calling-conventions".to_string(),
});
}
2022-12-20 16:15:55 +00:00
pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness {
match c {
Const::Yes(_) => hir::Constness::Const,
Const::No => hir::Constness::NotConst,
}
}
pub(super) fn lower_safety(&mut self, s: Safety, default: hir::Safety) -> hir::Safety {
2024-05-17 14:17:48 -03:00
match s {
Safety::Unsafe(_) => hir::Safety::Unsafe,
Safety::Default => default,
Safety::Safe(_) => hir::Safety::Safe,
}
}
2022-05-31 17:40:50 -03:00
/// Return the pair of the lowered `generics` as `hir::Generics` and the evaluation of `f` with
/// the carried impl trait definitions and bounds.
#[instrument(level = "debug", skip(self, f))]
fn lower_generics<T>(
&mut self,
generics: &Generics,
constness: Const,
2024-06-14 12:16:15 +00:00
force_append_constness: bool,
parent_node_id: NodeId,
itctx: ImplTraitContext,
f: impl FnOnce(&mut Self) -> T,
) -> (&'hir hir::Generics<'hir>, T) {
debug_assert!(self.impl_trait_defs.is_empty());
debug_assert!(self.impl_trait_bounds.is_empty());
2022-03-01 20:02:47 +08:00
// Error if `?Trait` bounds in where clauses don't refer directly to type parameters.
// Note: we used to clone these bounds directly onto the type parameter (and avoid lowering
// these into hir when we lower thee where clauses), but this makes it quite difficult to
// keep track of the Span info. Now, `<dyn HirTyLowerer>::add_implicit_sized_bound`
// checks both param bounds and where clauses for `?Sized`.
2019-08-10 20:13:12 +02:00
for pred in &generics.where_clause.predicates {
let WherePredicate::BoundPredicate(bound_pred) = pred else {
2022-02-19 00:48:49 +01:00
continue;
};
let compute_is_param = || {
// Check if the where clause type is a plain type parameter.
match self
.resolver
.get_partial_res(bound_pred.bounded_ty.id)
.and_then(|r| r.full_res())
{
Some(Res::Def(DefKind::TyParam, def_id))
if bound_pred.bound_generic_params.is_empty() =>
{
generics
.params
.iter()
2021-07-18 20:09:20 +02:00
.any(|p| def_id == self.local_def_id(p.id).to_def_id())
2019-08-10 20:13:12 +02:00
}
// Either the `bounded_ty` is not a plain type parameter, or
// it's not found in the generic type parameters list.
_ => false,
}
};
// We only need to compute this once per `WherePredicate`, but don't
// need to compute this at all unless there is a Maybe bound.
let mut is_param: Option<bool> = None;
for bound in &bound_pred.bounds {
2023-12-20 15:22:06 +01:00
if !matches!(
*bound,
GenericBound::Trait(
_,
TraitBoundModifiers { polarity: BoundPolarity::Maybe(_), .. }
)
) {
continue;
}
let is_param = *is_param.get_or_insert_with(compute_is_param);
if !is_param {
self.dcx().emit_err(MisplacedRelaxTraitBound { span: bound.span() });
2019-08-10 20:13:12 +02:00
}
}
}
// Desugar `~const` bound in generics into an additional `const host: bool` param
// if the effects feature is enabled. This needs to be done before we lower where
// clauses since where clauses need to bind to the DefId of the host param
let host_param_parts = if let Const::Yes(span) = constness
2024-06-14 12:16:15 +00:00
// if this comes from implementing a `const` trait, we must force constness to be appended
// to the impl item, no matter whether effects is enabled.
&& (self.tcx.features().effects || force_append_constness)
{
2023-12-09 00:00:53 +00:00
let span = self.lower_span(span);
let param_node_id = self.next_node_id();
let hir_id = self.next_id();
let def_id = self.create_def(
self.local_def_id(parent_node_id),
param_node_id,
sym::host,
DefKind::ConstParam,
span,
);
self.host_param_id = Some(def_id);
Some((span, hir_id, def_id))
} else {
None
};
let mut predicates: SmallVec<[hir::WherePredicate<'hir>; 4]> = SmallVec::new();
predicates.extend(generics.params.iter().filter_map(|param| {
self.lower_generic_bound_predicate(
param.ident,
param.id,
&param.kind,
&param.bounds,
2022-12-25 22:16:04 +01:00
param.colon_span,
generics.span,
itctx,
PredicateOrigin::GenericParam,
)
}));
predicates.extend(
2022-02-05 15:48:02 +01:00
generics
.where_clause
.predicates
.iter()
.map(|predicate| self.lower_where_predicate(predicate)),
);
let mut params: SmallVec<[hir::GenericParam<'hir>; 4]> = self
.lower_generic_params_mut(&generics.params, hir::GenericParamSource::Generics)
.collect();
// Introduce extra lifetimes if late resolution tells us to.
let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id);
params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| {
self.lifetime_res_to_generic_param(
ident,
node_id,
res,
hir::GenericParamSource::Generics,
)
}));
2022-06-05 17:37:45 -07:00
let has_where_clause_predicates = !generics.where_clause.predicates.is_empty();
let where_clause_span = self.lower_span(generics.where_clause.span);
let span = self.lower_span(generics.span);
let res = f(self);
let impl_trait_defs = std::mem::take(&mut self.impl_trait_defs);
params.extend(impl_trait_defs.into_iter());
let impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds);
predicates.extend(impl_trait_bounds.into_iter());
if let Some((span, hir_id, def_id)) = host_param_parts {
let const_node_id = self.next_node_id();
let anon_const_did =
self.create_def(def_id, const_node_id, kw::Empty, DefKind::AnonConst, span);
let const_id = self.next_id();
let const_expr_id = self.next_id();
let bool_id = self.next_id();
self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id)));
self.children.push((anon_const_did, hir::MaybeOwner::NonOwner(const_id)));
let const_body = self.lower_body(|this| {
(
&[],
hir::Expr {
hir_id: const_expr_id,
kind: hir::ExprKind::Lit(
this.arena.alloc(hir::Lit { node: LitKind::Bool(true), span }),
),
span,
},
)
});
let default_ac = self.arena.alloc(hir::AnonConst {
def_id: anon_const_did,
hir_id: const_id,
body: const_body,
span,
});
let default_ct = self.arena.alloc(hir::ConstArg {
kind: hir::ConstArgKind::Anon(default_ac),
is_desugared_from_effects: true,
});
let param = hir::GenericParam {
def_id,
hir_id,
name: hir::ParamName::Plain(Ident { name: sym::host, span }),
span,
kind: hir::GenericParamKind::Const {
ty: self.arena.alloc(self.ty(
span,
hir::TyKind::Path(hir::QPath::Resolved(
None,
self.arena.alloc(hir::Path {
res: Res::PrimTy(hir::PrimTy::Bool),
span,
segments: self.arena.alloc_from_iter([hir::PathSegment {
ident: Ident { name: sym::bool, span },
hir_id: bool_id,
res: Res::PrimTy(hir::PrimTy::Bool),
args: None,
infer_args: false,
}]),
}),
)),
)),
default: Some(default_ct),
is_host_effect: true,
2024-06-14 12:16:15 +00:00
synthetic: true,
},
colon_span: None,
pure_wrt_drop: false,
source: hir::GenericParamSource::Generics,
};
params.push(param);
}
let lowered_generics = self.arena.alloc(hir::Generics {
params: self.arena.alloc_from_iter(params),
predicates: self.arena.alloc_from_iter(predicates),
2022-06-05 17:37:45 -07:00
has_where_clause_predicates,
where_clause_span,
span,
});
(lowered_generics, res)
2019-08-10 20:13:12 +02:00
}
pub(super) fn lower_generic_bound_predicate(
&mut self,
ident: Ident,
id: NodeId,
kind: &GenericParamKind,
bounds: &[GenericBound],
2022-12-25 22:16:04 +01:00
colon_span: Option<Span>,
parent_span: Span,
itctx: ImplTraitContext,
origin: PredicateOrigin,
) -> Option<hir::WherePredicate<'hir>> {
// Do not create a clause if we do not have anything inside it.
if bounds.is_empty() {
return None;
}
2022-07-26 15:50:25 -03:00
let bounds = self.lower_param_bounds(bounds, itctx);
let ident = self.lower_ident(ident);
let param_span = ident.span;
2022-12-25 22:16:04 +01:00
// Reconstruct the span of the entire predicate from the individual generic bounds.
let span_start = colon_span.unwrap_or_else(|| param_span.shrink_to_hi());
let span = bounds.iter().fold(span_start, |span_accum, bound| {
match bound.span().find_ancestor_inside(parent_span) {
Some(bound_span) => span_accum.to(bound_span),
None => span_accum,
}
});
let span = self.lower_span(span);
match kind {
GenericParamKind::Const { .. } => None,
GenericParamKind::Type { .. } => {
2021-07-18 20:09:20 +02:00
let def_id = self.local_def_id(id).to_def_id();
let hir_id = self.next_id();
let res = Res::Def(DefKind::TyParam, def_id);
let ty_path = self.arena.alloc(hir::Path {
span: param_span,
res,
segments: self
.arena
2022-09-01 08:44:20 +10:00
.alloc_from_iter([hir::PathSegment::new(ident, hir_id, res)]),
});
let ty_id = self.next_id();
let bounded_ty =
self.ty_path(ty_id, param_span, hir::QPath::Resolved(None, ty_path));
Some(hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
hir_id: self.next_id(),
bounded_ty: self.arena.alloc(bounded_ty),
bounds,
span,
bound_generic_params: &[],
origin,
}))
}
GenericParamKind::Lifetime => {
let ident = self.lower_ident(ident);
2021-07-18 20:09:20 +02:00
let lt_id = self.next_node_id();
let lifetime = self.new_named_lifetime(id, lt_id, ident);
Some(hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
lifetime,
span,
bounds,
in_where_clause: false,
}))
}
}
}
2019-11-30 17:46:46 +01:00
fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> {
match pred {
2019-08-10 20:13:12 +02:00
WherePredicate::BoundPredicate(WhereBoundPredicate {
bound_generic_params,
bounded_ty,
bounds,
2019-08-10 20:13:12 +02:00
span,
}) => hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
hir_id: self.next_id(),
bound_generic_params: self
.lower_generic_params(bound_generic_params, hir::GenericParamSource::Binder),
bounded_ty: self
.lower_ty(bounded_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound)),
2023-10-11 08:25:08 +00:00
bounds: self.lower_param_bounds(
bounds,
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
2023-10-11 08:25:08 +00:00
),
span: self.lower_span(*span),
origin: PredicateOrigin::WhereClause,
}),
WherePredicate::RegionPredicate(WhereRegionPredicate { lifetime, bounds, span }) => {
hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
span: self.lower_span(*span),
lifetime: self.lower_lifetime(lifetime),
bounds: self.lower_param_bounds(
bounds,
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
),
in_where_clause: true,
})
}
WherePredicate::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty, span }) => {
2019-08-10 20:13:12 +02:00
hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
lhs_ty: self
.lower_ty(lhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound)),
rhs_ty: self
.lower_ty(rhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound)),
span: self.lower_span(*span),
2019-08-10 20:13:12 +02:00
})
}
}
}
}