Skip to content

Commit

Permalink
Rollup merge of rust-lang#137686 - nbdd0121:asm_const, r=compiler-errors
Browse files Browse the repository at this point in the history
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 rust-lang#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 rust-lang#128464 is implemented. This PR is a preparatory PR for that feature.

As an unintentional side effect, fix rust-lang#117877.

cc `@Amanieu`
r? `@compiler-errors`
  • Loading branch information
matthiaskrgr authored Feb 28, 2025
2 parents 32405da + 395b0fb commit 59a5466
Show file tree
Hide file tree
Showing 18 changed files with 133 additions and 100 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
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 } => {
let static_def_id = self
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3506,7 +3506,7 @@ pub enum InlineAsmOperand<'hir> {
out_expr: Option<&'hir Expr<'hir>>,
},
Const {
anon_const: &'hir AnonConst,
anon_const: ConstBlock,
},
SymFn {
expr: &'hir Expr<'hir>,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1447,7 +1447,7 @@ pub fn walk_inline_asm<'v, V: Visitor<'v>>(
visit_opt!(visitor, visit_expr, out_expr);
}
InlineAsmOperand::Const { anon_const, .. } => {
try_visit!(visitor.visit_anon_const(anon_const));
try_visit!(visitor.visit_inline_const(anon_const));
}
InlineAsmOperand::SymFn { expr, .. } => {
try_visit!(visitor.visit_expr(expr));
Expand Down
34 changes: 25 additions & 9 deletions compiler/rustc_hir_analysis/src/check/intrinsicck.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::assert_matches::debug_assert_matches;

use rustc_abi::FieldIdx;
use rustc_ast::InlineAsmTemplatePiece;
use rustc_data_structures::fx::FxIndexSet;
Expand All @@ -21,6 +19,7 @@ pub struct InlineAsmCtxt<'a, 'tcx: 'a> {
typing_env: ty::TypingEnv<'tcx>,
target_features: &'tcx FxIndexSet<Symbol>,
expr_ty: Box<dyn Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a>,
node_ty: Box<dyn Fn(hir::HirId) -> Ty<'tcx> + 'a>,
}

enum NonAsmTypeReason<'tcx> {
Expand All @@ -35,20 +34,26 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
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 {
InlineAsmCtxt {
tcx,
typing_env,
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),
}
}

fn expr_ty(&self, expr: &hir::Expr<'tcx>) -> Ty<'tcx> {
(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 == ()`
fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
// Type still may have region variables, but `Sized` does not depend
Expand Down Expand Up @@ -487,12 +492,23 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
);
}
}
// Typeck has checked that Const operands are integers.
hir::InlineAsmOperand::Const { anon_const } => {
debug_assert_matches!(
self.tcx.type_of(anon_const.def_id).instantiate_identity().kind(),
ty::Error(_) | ty::Int(_) | ty::Uint(_)
);
let ty = self.node_ty(anon_const.hir_id);
match ty.kind() {
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.
hir::InlineAsmOperand::SymFn { expr } => {
Expand Down
11 changes: 0 additions & 11 deletions compiler/rustc_hir_analysis/src/collect/generics_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,17 +186,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
{
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),
_ => None,
}
Expand Down
34 changes: 1 addition & 33 deletions compiler/rustc_hir_analysis/src/collect/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rustc_middle::query::plumbing::CyclePlaceholder;
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::print::with_forced_trimmed_paths;
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_span::{DUMMY_SP, Ident, Span};

Expand All @@ -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 = 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 {
// Anon consts "inside" the type system.
Node::ConstArg(&ConstArg {
Expand All @@ -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),

// 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 => {
tcx.adt_def(tcx.hir_get_parent_item(hir_id)).repr().discr_type().to_ty(tcx)
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1414,7 +1414,8 @@ impl<'a> State<'a> {
hir::InlineAsmOperand::Const { ref anon_const } => {
s.word("const");
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 } => {
s.word("sym_fn");
Expand Down
7 changes: 3 additions & 4 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3774,13 +3774,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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 } => {
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::Label { block } => {
let previous_diverges = self.diverges.get();
Expand Down
11 changes: 9 additions & 2 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.erase_regions(ty)
}
};
InlineAsmCtxt::new(self.tcx, enclosing_id, self.typing_env(self.param_env), expr_ty)
.check_asm(asm);
let node_ty = |hir_id: HirId| self.typeck_results.borrow().node_type(hir_id);
InlineAsmCtxt::new(
self.tcx,
enclosing_id,
self.typing_env(self.param_env),
expr_ty,
node_ty,
)
.check_asm(asm);
}
}

Expand Down
21 changes: 8 additions & 13 deletions compiler/rustc_hir_typeck/src/writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

///////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -284,9 +277,6 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => {
self.visit_field_id(e.hir_id);
}
hir::ExprKind::ConstBlock(ref anon_const) => {
self.visit_const_block(e.span, anon_const);
}
_ => {}
}

Expand All @@ -297,6 +287,14 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
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>) {
match &p.kind {
hir::GenericParamKind::Lifetime { .. } => {
Expand Down Expand Up @@ -343,9 +341,6 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {

fn visit_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) {
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);
}

Expand Down
20 changes: 14 additions & 6 deletions compiler/rustc_mir_build/src/thir/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,12 +730,20 @@ impl<'tcx> ThirBuildCx<'tcx> {
}
}
hir::InlineAsmOperand::Const { ref anon_const } => {
let value =
mir::Const::from_unevaluated(tcx, anon_const.def_id.to_def_id())
.instantiate_identity();
let span = tcx.def_span(anon_const.def_id);

InlineAsmOperand::Const { value, span }
let ty = self.typeck_results.node_type(anon_const.hir_id);
let did = anon_const.def_id.to_def_id();
let typeck_root_def_id = tcx.typeck_root_def_id(did);
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;

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 } => {
InlineAsmOperand::SymFn { value: self.mirror_expr(expr) }
Expand Down
39 changes: 39 additions & 0 deletions compiler/rustc_resolve/src/def_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,4 +459,43 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
visit::walk_attribute(self, 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),
}
}
}
}
13 changes: 0 additions & 13 deletions tests/crashes/117877.rs

This file was deleted.

10 changes: 10 additions & 0 deletions tests/ui/asm/const-resolve-error.rs
Original file line number Diff line number Diff line change
@@ -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() {}
14 changes: 14 additions & 0 deletions tests/ui/asm/const-resolve-error.stderr
Original file line number Diff line number Diff line change
@@ -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`.
4 changes: 2 additions & 2 deletions tests/ui/asm/fail-const-eval-issue-121099.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ use std::arch::global_asm;

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
4 changes: 2 additions & 2 deletions tests/ui/asm/fail-const-eval-issue-121099.stderr
Original file line number Diff line number Diff line change
@@ -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
|
LL | global_asm!("/* {} */", const 1 << 500);
| ^^^^^^^^ 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
|
LL | global_asm!("/* {} */", const 1 / 0);
Expand Down
Loading

0 comments on commit 59a5466

Please sign in to comment.