Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yeet mir::Const::from_anon_const #116446

Merged
merged 3 commits into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 14 additions & 99 deletions compiler/rustc_middle/src/mir/consts.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
use std::fmt::{self, Debug, Display, Formatter};

use rustc_hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir};
use rustc_hir::def_id::DefId;
use rustc_session::RemapFileNameExt;
use rustc_span::Span;
use rustc_target::abi::{HasDataLayout, Size};

use crate::mir::interpret::{alloc_range, AllocId, ConstAllocation, ErrorHandled, Scalar};
use crate::mir::{pretty_print_const_value, Promoted};
use crate::ty::GenericArgsRef;
use crate::ty::ScalarInt;
use crate::ty::{self, print::pretty_print_const, List, Ty, TyCtxt};
use crate::ty::{GenericArgs, GenericArgsRef};
use crate::ty::{self, print::pretty_print_const, Ty, TyCtxt};

///////////////////////////////////////////////////////////////////////////
/// Evaluated Constants
Expand Down Expand Up @@ -220,6 +219,17 @@ pub enum Const<'tcx> {
}

impl<'tcx> Const<'tcx> {
pub fn identity_unevaluated(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::EarlyBinder<Const<'tcx>> {
ty::EarlyBinder::bind(Const::Unevaluated(
UnevaluatedConst {
def: def_id,
args: ty::GenericArgs::identity_for_item(tcx, def_id),
promoted: None,
},
tcx.type_of(def_id).skip_binder(),
))
}

#[inline(always)]
pub fn ty(&self) -> Ty<'tcx> {
match self {
Expand Down Expand Up @@ -399,101 +409,6 @@ impl<'tcx> Const<'tcx> {
Self::Val(val, ty)
}

/// Literals are converted to `Const::Val`, const generic parameters are eagerly
/// converted to a constant, everything else becomes `Unevaluated`.
#[instrument(skip(tcx), level = "debug", ret)]
pub fn from_anon_const(
tcx: TyCtxt<'tcx>,
def: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
let body_id = match tcx.hir().get_by_def_id(def) {
hir::Node::AnonConst(ac) => ac.body,
_ => {
span_bug!(tcx.def_span(def), "from_anon_const can only process anonymous constants")
}
};

let expr = &tcx.hir().body(body_id).value;
debug!(?expr);

// Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
// currently have to be wrapped in curly brackets, so it's necessary to special-case.
let expr = match &expr.kind {
hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => {
block.expr.as_ref().unwrap()
}
_ => expr,
};
debug!("expr.kind: {:?}", expr.kind);

let ty = tcx.type_of(def).instantiate_identity();
debug!(?ty);

// FIXME(const_generics): We currently have to special case parameters because `min_const_generics`
// does not provide the parents generics to anonymous constants. We still allow generic const
// parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to
// ever try to substitute the generic parameters in their bodies.
//
// While this doesn't happen as these constants are always used as `ty::ConstKind::Param`, it does
// cause issues if we were to remove that special-case and try to evaluate the constant instead.
use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath};
match expr.kind {
ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => {
// Find the name and index of the const parameter by indexing the generics of
// the parent item and construct a `ParamConst`.
let item_def_id = tcx.parent(def_id);
let generics = tcx.generics_of(item_def_id);
let index = generics.param_def_id_to_index[&def_id];
let name = tcx.item_name(def_id);
let ty_const = ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty);
debug!(?ty_const);

return Self::Ty(ty_const);
}
_ => {}
}

let hir_id = tcx.hir().local_def_id_to_hir_id(def);
let parent_args = if let Some(parent_hir_id) = tcx.hir().opt_parent_id(hir_id)
&& let Some(parent_did) = parent_hir_id.as_owner()
{
GenericArgs::identity_for_item(tcx, parent_did)
} else {
List::empty()
};
debug!(?parent_args);

let did = def.to_def_id();
let child_args = GenericArgs::identity_for_item(tcx, did);
let args = tcx.mk_args_from_iter(parent_args.into_iter().chain(child_args.into_iter()));
debug!(?args);

let span = tcx.def_span(def);
let uneval = UnevaluatedConst::new(did, args);
debug!(?span, ?param_env);

match tcx.const_eval_resolve(param_env, uneval, Some(span)) {
Ok(val) => {
debug!("evaluated const value");
Self::Val(val, ty)
}
Err(_) => {
debug!("error encountered during evaluation");
// Error was handled in `const_eval_resolve`. Here we just create a
// new unevaluated const and error hard later in codegen
Self::Unevaluated(
UnevaluatedConst {
def: did,
args: GenericArgs::identity_for_item(tcx, did),
promoted: None,
},
ty,
)
}
}
}

pub fn from_ty_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
match c.kind() {
ty::ConstKind::Value(valtree) => {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ impl<'tcx> Const<'tcx> {
}
}

// FIXME(const_generics): We currently have to special case parameters because `min_const_generics`
// does not provide the parents generics to anonymous constants. We still allow generic const
// parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to
// ever try to substitute the generic parameters in their bodies.
match expr.kind {
hir::ExprKind::Path(hir::QPath::Resolved(
_,
Expand Down
16 changes: 12 additions & 4 deletions compiler/rustc_mir_build/src/thir/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,15 +642,23 @@ impl<'tcx> Cx<'tcx> {
}
}
hir::InlineAsmOperand::Const { ref anon_const } => {
let value =
mir::Const::from_anon_const(tcx, anon_const.def_id, self.param_env);
let value = mir::Const::identity_unevaluated(
tcx,
anon_const.def_id.to_def_id(),
)
.instantiate_identity()
.normalize(tcx, self.param_env);
let span = tcx.def_span(anon_const.def_id);

InlineAsmOperand::Const { value, span }
}
hir::InlineAsmOperand::SymFn { ref anon_const } => {
let value =
mir::Const::from_anon_const(tcx, anon_const.def_id, self.param_env);
let value = mir::Const::identity_unevaluated(
tcx,
anon_const.def_id.to_def_id(),
)
.instantiate_identity()
.normalize(tcx, self.param_env);
let span = tcx.def_span(anon_const.def_id);

InlineAsmOperand::SymFn { value, span }
Expand Down
15 changes: 15 additions & 0 deletions tests/ui/asm/const-error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// only-x86_64
// needs-asm-support

#![feature(asm_const)]

// Test to make sure that we emit const errors eagerly for inline asm

use std::arch::asm;

fn test<T>() {
unsafe { asm!("/* {} */", const 1 / 0); }
//~^ ERROR evaluation of
}

fn main() {}
9 changes: 9 additions & 0 deletions tests/ui/asm/const-error.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0080]: evaluation of `test::<T>::{constant#0}` failed
--> $DIR/const-error.rs:11:37
|
LL | unsafe { asm!("/* {} */", const 1 / 0); }
| ^^^^^ attempt to divide `1_i32` by zero

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0080`.
Loading