1
Fork 0

Rollup merge of #137686 - nbdd0121:asm_const, r=compiler-errors

Handle asm const similar to inline const

Previously, asm consts are handled similar to anon consts rather than inline consts. Anon consts are not good at dealing with lifetimes, because `type_of` has lifetimes erased already. Inline consts can deal with lifetimes because they live in an outer typeck context. And since `global_asm!` lacks an outer typeck context, we have implemented asm consts with anon consts while they're in fact more similar to inline consts.

This was changed in #137180, and this means that handling asm consts as inline consts are possible. While as `@compiler-errors` pointed out, `const` currently can't be used with any types with lifetime, this is about to change if #128464 is implemented. This PR is a preparatory PR for that feature.

As an unintentional side effect, fix #117877.

cc `@Amanieu`
r? `@compiler-errors`
This commit is contained in:
Matthias Krüger 2025-03-01 05:49:52 +01:00 committed by GitHub
commit 174bbfb369
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 132 additions and 99 deletions

View file

@ -195,7 +195,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} }
} }
InlineAsmOperand::Const { anon_const } => hir::InlineAsmOperand::Const { InlineAsmOperand::Const { anon_const } => hir::InlineAsmOperand::Const {
anon_const: self.lower_anon_const_to_anon_const(anon_const), anon_const: self.lower_const_block(anon_const),
}, },
InlineAsmOperand::Sym { sym } => { InlineAsmOperand::Sym { sym } => {
let static_def_id = self let static_def_id = self

View file

@ -3512,7 +3512,7 @@ pub enum InlineAsmOperand<'hir> {
out_expr: Option<&'hir Expr<'hir>>, out_expr: Option<&'hir Expr<'hir>>,
}, },
Const { Const {
anon_const: &'hir AnonConst, anon_const: ConstBlock,
}, },
SymFn { SymFn {
expr: &'hir Expr<'hir>, expr: &'hir Expr<'hir>,

View file

@ -1447,7 +1447,7 @@ pub fn walk_inline_asm<'v, V: Visitor<'v>>(
visit_opt!(visitor, visit_expr, out_expr); visit_opt!(visitor, visit_expr, out_expr);
} }
InlineAsmOperand::Const { anon_const, .. } => { InlineAsmOperand::Const { anon_const, .. } => {
try_visit!(visitor.visit_anon_const(anon_const)); try_visit!(visitor.visit_inline_const(anon_const));
} }
InlineAsmOperand::SymFn { expr, .. } => { InlineAsmOperand::SymFn { expr, .. } => {
try_visit!(visitor.visit_expr(expr)); try_visit!(visitor.visit_expr(expr));

View file

@ -1,5 +1,3 @@
use std::assert_matches::debug_assert_matches;
use rustc_abi::FieldIdx; use rustc_abi::FieldIdx;
use rustc_ast::InlineAsmTemplatePiece; use rustc_ast::InlineAsmTemplatePiece;
use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::fx::FxIndexSet;
@ -21,6 +19,7 @@ pub struct InlineAsmCtxt<'a, 'tcx: 'a> {
typing_env: ty::TypingEnv<'tcx>, typing_env: ty::TypingEnv<'tcx>,
target_features: &'tcx FxIndexSet<Symbol>, target_features: &'tcx FxIndexSet<Symbol>,
expr_ty: Box<dyn Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a>, expr_ty: Box<dyn Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a>,
node_ty: Box<dyn Fn(hir::HirId) -> Ty<'tcx> + 'a>,
} }
enum NonAsmTypeReason<'tcx> { enum NonAsmTypeReason<'tcx> {
@ -35,13 +34,15 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
def_id: LocalDefId, def_id: LocalDefId,
typing_env: ty::TypingEnv<'tcx>, typing_env: ty::TypingEnv<'tcx>,
get_operand_ty: impl Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a, expr_ty: impl Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a,
node_ty: impl Fn(hir::HirId) -> Ty<'tcx> + 'a,
) -> Self { ) -> Self {
InlineAsmCtxt { InlineAsmCtxt {
tcx, tcx,
typing_env, typing_env,
target_features: tcx.asm_target_features(def_id), target_features: tcx.asm_target_features(def_id),
expr_ty: Box::new(get_operand_ty), expr_ty: Box::new(expr_ty),
node_ty: Box::new(node_ty),
} }
} }
@ -49,6 +50,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
(self.expr_ty)(expr) (self.expr_ty)(expr)
} }
fn node_ty(&self, hir_id: hir::HirId) -> Ty<'tcx> {
(self.node_ty)(hir_id)
}
// FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()` // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool { fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
// Type still may have region variables, but `Sized` does not depend // Type still may have region variables, but `Sized` does not depend
@ -487,12 +492,23 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
); );
} }
} }
// Typeck has checked that Const operands are integers.
hir::InlineAsmOperand::Const { anon_const } => { hir::InlineAsmOperand::Const { anon_const } => {
debug_assert_matches!( let ty = self.node_ty(anon_const.hir_id);
self.tcx.type_of(anon_const.def_id).instantiate_identity().kind(), match ty.kind() {
ty::Error(_) | ty::Int(_) | ty::Uint(_) ty::Error(_) => {}
); _ if ty.is_integral() => {}
_ => {
self.tcx
.dcx()
.struct_span_err(op_sp, "invalid type for `const` operand")
.with_span_label(
self.tcx.def_span(anon_const.def_id),
format!("is {} `{}`", ty.kind().article(), ty),
)
.with_help("`const` operands must be of an integer type")
.emit();
}
}
} }
// Typeck has checked that SymFn refers to a function. // Typeck has checked that SymFn refers to a function.
hir::InlineAsmOperand::SymFn { expr } => { hir::InlineAsmOperand::SymFn { expr } => {

View file

@ -186,17 +186,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
{ {
Some(parent_did) Some(parent_did)
} }
// Exclude `GlobalAsm` here which cannot have generics.
Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
if asm.operands.iter().any(|(op, _op_sp)| match op {
hir::InlineAsmOperand::Const { anon_const } => {
anon_const.hir_id == hir_id
}
_ => false,
}) =>
{
Some(parent_did)
}
Node::TyPat(_) => Some(parent_did), Node::TyPat(_) => Some(parent_did),
_ => None, _ => None,
} }

View file

@ -8,7 +8,7 @@ use rustc_middle::query::plumbing::CyclePlaceholder;
use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::{bug, span_bug}; use rustc_middle::{bug, span_bug};
use rustc_span::{DUMMY_SP, Ident, Span}; use rustc_span::{DUMMY_SP, Ident, Span};
@ -35,13 +35,6 @@ fn anon_const_type_of<'tcx>(icx: &ItemCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx
let parent_node_id = tcx.parent_hir_id(hir_id); let parent_node_id = tcx.parent_hir_id(hir_id);
let parent_node = tcx.hir_node(parent_node_id); let parent_node = tcx.hir_node(parent_node_id);
let find_const = |&(op, op_sp)| match op {
hir::InlineAsmOperand::Const { anon_const } if anon_const.hir_id == hir_id => {
Some((anon_const, op_sp))
}
_ => None,
};
match parent_node { match parent_node {
// Anon consts "inside" the type system. // Anon consts "inside" the type system.
Node::ConstArg(&ConstArg { Node::ConstArg(&ConstArg {
@ -50,31 +43,6 @@ fn anon_const_type_of<'tcx>(icx: &ItemCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx
.. ..
}) if anon_hir_id == hir_id => const_arg_anon_type_of(icx, arg_hir_id, span), }) if anon_hir_id == hir_id => const_arg_anon_type_of(icx, arg_hir_id, span),
// Anon consts outside the type system.
Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
| Node::Item(&Item { kind: ItemKind::GlobalAsm { asm, .. }, .. })
if let Some((anon_const, op_sp)) = asm.operands.iter().find_map(find_const) =>
{
let ty = tcx.typeck(def_id).node_type(hir_id);
match ty.kind() {
ty::Error(_) => ty,
ty::Int(_) | ty::Uint(_) => ty,
_ => {
let guar = tcx
.dcx()
.struct_span_err(op_sp, "invalid type for `const` operand")
.with_span_label(
tcx.def_span(anon_const.def_id),
format!("is {} `{}`", ty.kind().article(), ty),
)
.with_help("`const` operands must be of an integer type")
.emit();
Ty::new_error(tcx, guar)
}
}
}
Node::Variant(Variant { disr_expr: Some(e), .. }) if e.hir_id == hir_id => { Node::Variant(Variant { disr_expr: Some(e), .. }) if e.hir_id == hir_id => {
tcx.adt_def(tcx.hir_get_parent_item(hir_id)).repr().discr_type().to_ty(tcx) tcx.adt_def(tcx.hir_get_parent_item(hir_id)).repr().discr_type().to_ty(tcx)
} }

View file

@ -1413,7 +1413,8 @@ impl<'a> State<'a> {
hir::InlineAsmOperand::Const { ref anon_const } => { hir::InlineAsmOperand::Const { ref anon_const } => {
s.word("const"); s.word("const");
s.space(); s.space();
s.print_anon_const(anon_const); // Not using `print_inline_const` to avoid additional `const { ... }`
s.ann.nested(s, Nested::Body(anon_const.body))
} }
hir::InlineAsmOperand::SymFn { ref expr } => { hir::InlineAsmOperand::SymFn { ref expr } => {
s.word("sym_fn"); s.word("sym_fn");

View file

@ -3774,13 +3774,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_expr_asm_operand(out_expr, false); self.check_expr_asm_operand(out_expr, false);
} }
} }
hir::InlineAsmOperand::Const { ref anon_const } => {
self.check_expr_const_block(anon_const, Expectation::NoExpectation);
}
hir::InlineAsmOperand::SymFn { expr } => { hir::InlineAsmOperand::SymFn { expr } => {
self.check_expr(expr); self.check_expr(expr);
} }
// `AnonConst`s have their own body and is type-checked separately.
// As they don't flow into the type system we don't need them to
// be well-formed.
hir::InlineAsmOperand::Const { .. } => {}
hir::InlineAsmOperand::SymStatic { .. } => {} hir::InlineAsmOperand::SymStatic { .. } => {}
hir::InlineAsmOperand::Label { block } => { hir::InlineAsmOperand::Label { block } => {
let previous_diverges = self.diverges.get(); let previous_diverges = self.diverges.get();

View file

@ -110,8 +110,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.erase_regions(ty) self.tcx.erase_regions(ty)
} }
}; };
InlineAsmCtxt::new(self.tcx, enclosing_id, self.typing_env(self.param_env), expr_ty) let node_ty = |hir_id: HirId| self.typeck_results.borrow().node_type(hir_id);
.check_asm(asm); InlineAsmCtxt::new(
self.tcx,
enclosing_id,
self.typing_env(self.param_env),
expr_ty,
node_ty,
)
.check_asm(asm);
} }
} }

View file

@ -248,13 +248,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
} }
} }
} }
fn visit_const_block(&mut self, span: Span, anon_const: &hir::ConstBlock) {
self.visit_node_id(span, anon_const.hir_id);
let body = self.tcx().hir_body(anon_const.body);
self.visit_body(body);
}
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -284,9 +277,6 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => { hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => {
self.visit_field_id(e.hir_id); self.visit_field_id(e.hir_id);
} }
hir::ExprKind::ConstBlock(ref anon_const) => {
self.visit_const_block(e.span, anon_const);
}
_ => {} _ => {}
} }
@ -297,6 +287,14 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
self.fix_index_builtin_expr(e); self.fix_index_builtin_expr(e);
} }
fn visit_inline_const(&mut self, anon_const: &hir::ConstBlock) {
let span = self.tcx().def_span(anon_const.def_id);
self.visit_node_id(span, anon_const.hir_id);
let body = self.tcx().hir_body(anon_const.body);
self.visit_body(body);
}
fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
match &p.kind { match &p.kind {
hir::GenericParamKind::Lifetime { .. } => { hir::GenericParamKind::Lifetime { .. } => {
@ -340,9 +338,6 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
fn visit_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) { fn visit_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) {
self.visit_node_id(expr.span, expr.hir_id); self.visit_node_id(expr.span, expr.hir_id);
if let hir::PatExprKind::ConstBlock(c) = &expr.kind {
self.visit_const_block(expr.span, c);
}
intravisit::walk_pat_expr(self, expr); intravisit::walk_pat_expr(self, expr);
} }

View file

@ -730,12 +730,20 @@ impl<'tcx> ThirBuildCx<'tcx> {
} }
} }
hir::InlineAsmOperand::Const { ref anon_const } => { hir::InlineAsmOperand::Const { ref anon_const } => {
let value = let ty = self.typeck_results.node_type(anon_const.hir_id);
mir::Const::from_unevaluated(tcx, anon_const.def_id.to_def_id()) let did = anon_const.def_id.to_def_id();
.instantiate_identity(); let typeck_root_def_id = tcx.typeck_root_def_id(did);
let span = tcx.def_span(anon_const.def_id); let parent_args = tcx.erase_regions(GenericArgs::identity_for_item(
tcx,
typeck_root_def_id,
));
let args =
InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty })
.args;
InlineAsmOperand::Const { value, span } let uneval = mir::UnevaluatedConst::new(did, args);
let value = mir::Const::Unevaluated(uneval, ty);
InlineAsmOperand::Const { value, span: tcx.def_span(did) }
} }
hir::InlineAsmOperand::SymFn { expr } => { hir::InlineAsmOperand::SymFn { expr } => {
InlineAsmOperand::SymFn { value: self.mirror_expr(expr) } InlineAsmOperand::SymFn { value: self.mirror_expr(expr) }

View file

@ -459,4 +459,43 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
visit::walk_attribute(self, attr); visit::walk_attribute(self, attr);
self.in_attr = orig_in_attr; self.in_attr = orig_in_attr;
} }
fn visit_inline_asm(&mut self, asm: &'a InlineAsm) {
let InlineAsm {
asm_macro: _,
template: _,
template_strs: _,
operands,
clobber_abis: _,
options: _,
line_spans: _,
} = asm;
for (op, _span) in operands {
match op {
InlineAsmOperand::In { expr, reg: _ }
| InlineAsmOperand::Out { expr: Some(expr), reg: _, late: _ }
| InlineAsmOperand::InOut { expr, reg: _, late: _ } => {
self.visit_expr(expr);
}
InlineAsmOperand::Out { expr: None, reg: _, late: _ } => {}
InlineAsmOperand::SplitInOut { in_expr, out_expr, reg: _, late: _ } => {
self.visit_expr(in_expr);
if let Some(expr) = out_expr {
self.visit_expr(expr);
}
}
InlineAsmOperand::Const { anon_const } => {
let def = self.create_def(
anon_const.id,
kw::Empty,
DefKind::InlineConst,
anon_const.value.span,
);
self.with_parent(def, |this| visit::walk_anon_const(this, anon_const));
}
InlineAsmOperand::Sym { sym } => self.visit_inline_asm_sym(sym),
InlineAsmOperand::Label { block } => self.visit_block(block),
}
}
}
} }

View file

@ -1,13 +0,0 @@
//@ known-bug: #117877
//@ edition:2021
//@ needs-rustc-debug-assertions
//@ only-x86_64
#![feature(asm_const)]
use std::arch::asm;
async unsafe fn foo<'a>() {
asm!("/* {0} */", const N);
}
fn main() {}

View file

@ -0,0 +1,10 @@
//@ edition:2021
//@ needs-asm-support
use std::arch::asm;
async unsafe fn foo<'a>() {
asm!("/* {0} */", const N); //~ ERROR E0425
}
fn main() {}

View file

@ -0,0 +1,14 @@
error[E0425]: cannot find value `N` in this scope
--> $DIR/const-resolve-error.rs:7:29
|
LL | asm!("/* {0} */", const N);
| ^ not found in this scope
|
help: you might be missing a const parameter
|
LL | async unsafe fn foo<'a, const N: /* Type */>() {
| +++++++++++++++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0425`.

View file

@ -5,6 +5,6 @@ use std::arch::global_asm;
fn main() {} fn main() {}
global_asm!("/* {} */", const 1 << 500); //~ ERROR evaluation of constant value failed [E0080] global_asm!("/* {} */", const 1 << 500); //~ ERROR E0080
global_asm!("/* {} */", const 1 / 0); //~ ERROR evaluation of constant value failed [E0080] global_asm!("/* {} */", const 1 / 0); //~ ERROR E0080

View file

@ -1,10 +1,10 @@
error[E0080]: evaluation of constant value failed error[E0080]: evaluation of `{global_asm#0}::{constant#0}` failed
--> $DIR/fail-const-eval-issue-121099.rs:8:31 --> $DIR/fail-const-eval-issue-121099.rs:8:31
| |
LL | global_asm!("/* {} */", const 1 << 500); LL | global_asm!("/* {} */", const 1 << 500);
| ^^^^^^^^ attempt to shift left by `500_i32`, which would overflow | ^^^^^^^^ attempt to shift left by `500_i32`, which would overflow
error[E0080]: evaluation of constant value failed error[E0080]: evaluation of `{global_asm#1}::{constant#0}` failed
--> $DIR/fail-const-eval-issue-121099.rs:10:31 --> $DIR/fail-const-eval-issue-121099.rs:10:31
| |
LL | global_asm!("/* {} */", const 1 / 0); LL | global_asm!("/* {} */", const 1 / 0);

View file

@ -80,7 +80,7 @@ error: invalid type for `const` operand
LL | asm!("{}", const &0); LL | asm!("{}", const &0);
| ^^^^^^-- | ^^^^^^--
| | | |
| is a `&i32` | is a `&{integer}`
| |
= help: `const` operands must be of an integer type = help: `const` operands must be of an integer type