Auto merge of #120675 - oli-obk:intrinsics3.0, r=pnkfelix
Add a scheme for moving away from `extern "rust-intrinsic"` entirely All `rust-intrinsic`s can become free functions now, either with a fallback body, or with a dummy body and an attribute, requiring backends to actually implement the intrinsic. This PR demonstrates the dummy-body scheme with the `vtable_size` intrinsic. cc https://github.com/rust-lang/rust/issues/63585 follow-up to #120500 MCP at https://github.com/rust-lang/compiler-team/issues/720
This commit is contained in:
commit
2eeff462b7
@ -1667,7 +1667,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||||||
// (Eventually this should use const-generics, but those are not up for the task yet:
|
// (Eventually this should use const-generics, but those are not up for the task yet:
|
||||||
// https://github.com/rust-lang/rust/issues/85229.)
|
// https://github.com/rust-lang/rust/issues/85229.)
|
||||||
if let Some(name @ (sym::simd_shuffle | sym::simd_insert | sym::simd_extract)) =
|
if let Some(name @ (sym::simd_shuffle | sym::simd_insert | sym::simd_extract)) =
|
||||||
self.tcx().intrinsic(def_id)
|
self.tcx().intrinsic(def_id).map(|i| i.name)
|
||||||
{
|
{
|
||||||
let idx = match name {
|
let idx = match name {
|
||||||
sym::simd_shuffle => 2,
|
sym::simd_shuffle => 2,
|
||||||
|
@ -1255,7 +1255,17 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||||||
|
|
||||||
// Unimplemented intrinsics must have a fallback body. The fallback body is obtained
|
// Unimplemented intrinsics must have a fallback body. The fallback body is obtained
|
||||||
// by converting the `InstanceDef::Intrinsic` to an `InstanceDef::Item`.
|
// by converting the `InstanceDef::Intrinsic` to an `InstanceDef::Item`.
|
||||||
_ => return Err(Instance::new(instance.def_id(), instance.args)),
|
_ => {
|
||||||
|
let intrinsic = fx.tcx.intrinsic(instance.def_id()).unwrap();
|
||||||
|
if intrinsic.must_be_overridden {
|
||||||
|
span_bug!(
|
||||||
|
source_info.span,
|
||||||
|
"intrinsic {} must be overridden by codegen_cranelift, but isn't",
|
||||||
|
intrinsic.name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Err(Instance::new(instance.def_id(), instance.args));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret_block = fx.get_block(destination.unwrap());
|
let ret_block = fx.get_block(destination.unwrap());
|
||||||
|
@ -81,6 +81,10 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
|
|||||||
return library.kind.is_statically_included().then_some(def_id);
|
return library.kind.is_statically_included().then_some(def_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
// Only consider nodes that actually have exported symbols.
|
// Only consider nodes that actually have exported symbols.
|
||||||
match tcx.def_kind(def_id) {
|
match tcx.def_kind(def_id) {
|
||||||
DefKind::Fn | DefKind::Static(_) => {}
|
DefKind::Fn | DefKind::Static(_) => {}
|
||||||
|
@ -12,12 +12,12 @@ use crate::MemFlags;
|
|||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
||||||
use rustc_hir::lang_items::LangItem;
|
use rustc_hir::lang_items::LangItem;
|
||||||
use rustc_middle::mir::{self, AssertKind, SwitchTargets, UnwindTerminateReason};
|
use rustc_middle::mir::{self, AssertKind, BasicBlock, SwitchTargets, UnwindTerminateReason};
|
||||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement};
|
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement};
|
||||||
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
|
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
|
||||||
use rustc_middle::ty::{self, Instance, Ty};
|
use rustc_middle::ty::{self, Instance, Ty};
|
||||||
use rustc_session::config::OptLevel;
|
use rustc_session::config::OptLevel;
|
||||||
use rustc_span::{source_map::Spanned, sym, Span, Symbol};
|
use rustc_span::{source_map::Spanned, sym, Span};
|
||||||
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg};
|
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg};
|
||||||
use rustc_target::abi::{self, HasDataLayout, WrappingRange};
|
use rustc_target::abi::{self, HasDataLayout, WrappingRange};
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
@ -680,7 +680,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
helper: &TerminatorCodegenHelper<'tcx>,
|
helper: &TerminatorCodegenHelper<'tcx>,
|
||||||
bx: &mut Bx,
|
bx: &mut Bx,
|
||||||
intrinsic: Option<Symbol>,
|
intrinsic: Option<ty::IntrinsicDef>,
|
||||||
instance: Option<Instance<'tcx>>,
|
instance: Option<Instance<'tcx>>,
|
||||||
source_info: mir::SourceInfo,
|
source_info: mir::SourceInfo,
|
||||||
target: Option<mir::BasicBlock>,
|
target: Option<mir::BasicBlock>,
|
||||||
@ -690,7 +690,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||||||
// Emit a panic or a no-op for `assert_*` intrinsics.
|
// Emit a panic or a no-op for `assert_*` intrinsics.
|
||||||
// These are intrinsics that compile to panics so that we can get a message
|
// These are intrinsics that compile to panics so that we can get a message
|
||||||
// which mentions the offending type, even from a const context.
|
// which mentions the offending type, even from a const context.
|
||||||
let panic_intrinsic = intrinsic.and_then(|s| ValidityRequirement::from_intrinsic(s));
|
let panic_intrinsic = intrinsic.and_then(|i| ValidityRequirement::from_intrinsic(i.name));
|
||||||
if let Some(requirement) = panic_intrinsic {
|
if let Some(requirement) = panic_intrinsic {
|
||||||
let ty = instance.unwrap().args.type_at(0);
|
let ty = instance.unwrap().args.type_at(0);
|
||||||
|
|
||||||
@ -826,14 +826,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||||||
// The arguments we'll be passing. Plus one to account for outptr, if used.
|
// The arguments we'll be passing. Plus one to account for outptr, if used.
|
||||||
let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize;
|
let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize;
|
||||||
|
|
||||||
if intrinsic == Some(sym::caller_location) {
|
if matches!(intrinsic, Some(ty::IntrinsicDef { name: sym::caller_location, .. })) {
|
||||||
return if let Some(target) = target {
|
return if let Some(target) = target {
|
||||||
let location =
|
let location =
|
||||||
self.get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info });
|
self.get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info });
|
||||||
|
|
||||||
let mut llargs = Vec::with_capacity(arg_count);
|
let mut llargs = Vec::with_capacity(arg_count);
|
||||||
let ret_dest =
|
let ret_dest = self.make_return_dest(
|
||||||
self.make_return_dest(bx, destination, &fn_abi.ret, &mut llargs, true, true);
|
bx,
|
||||||
|
destination,
|
||||||
|
&fn_abi.ret,
|
||||||
|
&mut llargs,
|
||||||
|
intrinsic,
|
||||||
|
Some(target),
|
||||||
|
);
|
||||||
assert_eq!(llargs, []);
|
assert_eq!(llargs, []);
|
||||||
if let ReturnDest::IndirectOperand(tmp, _) = ret_dest {
|
if let ReturnDest::IndirectOperand(tmp, _) = ret_dest {
|
||||||
location.val.store(bx, tmp);
|
location.val.store(bx, tmp);
|
||||||
@ -846,7 +852,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let instance = match intrinsic {
|
let instance = match intrinsic {
|
||||||
None | Some(sym::drop_in_place) => instance,
|
None | Some(ty::IntrinsicDef { name: sym::drop_in_place, .. }) => instance,
|
||||||
Some(intrinsic) => {
|
Some(intrinsic) => {
|
||||||
let mut llargs = Vec::with_capacity(1);
|
let mut llargs = Vec::with_capacity(1);
|
||||||
let ret_dest = self.make_return_dest(
|
let ret_dest = self.make_return_dest(
|
||||||
@ -854,8 +860,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||||||
destination,
|
destination,
|
||||||
&fn_abi.ret,
|
&fn_abi.ret,
|
||||||
&mut llargs,
|
&mut llargs,
|
||||||
true,
|
Some(intrinsic),
|
||||||
target.is_some(),
|
target,
|
||||||
);
|
);
|
||||||
let dest = match ret_dest {
|
let dest = match ret_dest {
|
||||||
_ if fn_abi.ret.is_indirect() => llargs[0],
|
_ if fn_abi.ret.is_indirect() => llargs[0],
|
||||||
@ -873,7 +879,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||||||
// The indices passed to simd_shuffle in the
|
// The indices passed to simd_shuffle in the
|
||||||
// third argument must be constant. This is
|
// third argument must be constant. This is
|
||||||
// checked by the type-checker.
|
// checked by the type-checker.
|
||||||
if i == 2 && intrinsic == sym::simd_shuffle {
|
if i == 2 && intrinsic.name == sym::simd_shuffle {
|
||||||
if let mir::Operand::Constant(constant) = &arg.node {
|
if let mir::Operand::Constant(constant) = &arg.node {
|
||||||
let (llval, ty) = self.simd_shuffle_indices(bx, constant);
|
let (llval, ty) = self.simd_shuffle_indices(bx, constant);
|
||||||
return OperandRef {
|
return OperandRef {
|
||||||
@ -903,14 +909,33 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||||||
MergingSucc::False
|
MergingSucc::False
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Err(instance) => Some(instance),
|
Err(instance) => {
|
||||||
|
if intrinsic.must_be_overridden {
|
||||||
|
span_bug!(
|
||||||
|
span,
|
||||||
|
"intrinsic {} must be overridden by codegen backend, but isn't",
|
||||||
|
intrinsic.name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(instance)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut llargs = Vec::with_capacity(arg_count);
|
let mut llargs = Vec::with_capacity(arg_count);
|
||||||
let destination = target.as_ref().map(|&target| {
|
let destination = target.as_ref().map(|&target| {
|
||||||
(self.make_return_dest(bx, destination, &fn_abi.ret, &mut llargs, false, true), target)
|
(
|
||||||
|
self.make_return_dest(
|
||||||
|
bx,
|
||||||
|
destination,
|
||||||
|
&fn_abi.ret,
|
||||||
|
&mut llargs,
|
||||||
|
None,
|
||||||
|
Some(target),
|
||||||
|
),
|
||||||
|
target,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Split the rust-call tupled arguments off.
|
// Split the rust-call tupled arguments off.
|
||||||
@ -1643,10 +1668,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||||||
dest: mir::Place<'tcx>,
|
dest: mir::Place<'tcx>,
|
||||||
fn_ret: &ArgAbi<'tcx, Ty<'tcx>>,
|
fn_ret: &ArgAbi<'tcx, Ty<'tcx>>,
|
||||||
llargs: &mut Vec<Bx::Value>,
|
llargs: &mut Vec<Bx::Value>,
|
||||||
is_intrinsic: bool,
|
intrinsic: Option<ty::IntrinsicDef>,
|
||||||
has_target: bool,
|
target: Option<BasicBlock>,
|
||||||
) -> ReturnDest<'tcx, Bx::Value> {
|
) -> ReturnDest<'tcx, Bx::Value> {
|
||||||
if !has_target {
|
if target.is_none() {
|
||||||
return ReturnDest::Nothing;
|
return ReturnDest::Nothing;
|
||||||
}
|
}
|
||||||
// If the return is ignored, we can just return a do-nothing `ReturnDest`.
|
// If the return is ignored, we can just return a do-nothing `ReturnDest`.
|
||||||
@ -1667,7 +1692,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||||||
tmp.storage_live(bx);
|
tmp.storage_live(bx);
|
||||||
llargs.push(tmp.llval);
|
llargs.push(tmp.llval);
|
||||||
ReturnDest::IndirectOperand(tmp, index)
|
ReturnDest::IndirectOperand(tmp, index)
|
||||||
} else if is_intrinsic {
|
} else if intrinsic.is_some() {
|
||||||
// Currently, intrinsics always need a location to store
|
// Currently, intrinsics always need a location to store
|
||||||
// the result, so we create a temporary `alloca` for the
|
// the result, so we create a temporary `alloca` for the
|
||||||
// result.
|
// result.
|
||||||
|
@ -867,6 +867,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||||||
rustc_no_mir_inline, Normal, template!(Word), WarnFollowing,
|
rustc_no_mir_inline, Normal, template!(Word), WarnFollowing,
|
||||||
"#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen"
|
"#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen"
|
||||||
),
|
),
|
||||||
|
rustc_attr!(
|
||||||
|
rustc_intrinsic_must_be_overridden, Normal, template!(Word), ErrorFollowing,
|
||||||
|
"the `#[rustc_intrinsic_must_be_overridden]` attribute is used to declare intrinsics without real bodies",
|
||||||
|
),
|
||||||
|
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Internal attributes, Testing:
|
// Internal attributes, Testing:
|
||||||
|
@ -527,12 +527,12 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
|||||||
check_enum(tcx, def_id);
|
check_enum(tcx, def_id);
|
||||||
}
|
}
|
||||||
DefKind::Fn => {
|
DefKind::Fn => {
|
||||||
if let Some(name) = tcx.intrinsic(def_id) {
|
if let Some(i) = tcx.intrinsic(def_id) {
|
||||||
intrinsic::check_intrinsic_type(
|
intrinsic::check_intrinsic_type(
|
||||||
tcx,
|
tcx,
|
||||||
def_id,
|
def_id,
|
||||||
tcx.def_ident_span(def_id).unwrap(),
|
tcx.def_ident_span(def_id).unwrap(),
|
||||||
name,
|
i.name,
|
||||||
Abi::Rust,
|
Abi::Rust,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -545,7 +545,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
|
|
||||||
if let Some(def_id) = def_id
|
if let Some(def_id) = def_id
|
||||||
&& self.tcx.def_kind(def_id) == hir::def::DefKind::Fn
|
&& self.tcx.def_kind(def_id) == hir::def::DefKind::Fn
|
||||||
&& matches!(self.tcx.intrinsic(def_id), Some(sym::const_eval_select))
|
&& self.tcx.is_intrinsic(def_id, sym::const_eval_select)
|
||||||
{
|
{
|
||||||
let fn_sig = self.resolve_vars_if_possible(fn_sig);
|
let fn_sig = self.resolve_vars_if_possible(fn_sig);
|
||||||
for idx in 0..=1 {
|
for idx in 0..=1 {
|
||||||
|
@ -1231,7 +1231,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn def_id_is_transmute(cx: &LateContext<'_>, def_id: DefId) -> bool {
|
fn def_id_is_transmute(cx: &LateContext<'_>, def_id: DefId) -> bool {
|
||||||
matches!(cx.tcx.intrinsic(def_id), Some(sym::transmute))
|
cx.tcx.is_intrinsic(def_id, sym::transmute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1749,7 +1749,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||||||
self.root.tables.attr_flags.get(self, index)
|
self.root.tables.attr_flags.get(self, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_intrinsic(self, index: DefIndex) -> Option<Symbol> {
|
fn get_intrinsic(self, index: DefIndex) -> Option<ty::IntrinsicDef> {
|
||||||
self.root.tables.intrinsic.get(self, index).map(|d| d.decode(self))
|
self.root.tables.intrinsic.get(self, index).map(|d| d.decode(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1053,11 +1053,14 @@ fn should_encode_mir(
|
|||||||
// Full-fledged functions + closures
|
// Full-fledged functions + closures
|
||||||
DefKind::AssocFn | DefKind::Fn | DefKind::Closure => {
|
DefKind::AssocFn | DefKind::Fn | DefKind::Closure => {
|
||||||
let generics = tcx.generics_of(def_id);
|
let generics = tcx.generics_of(def_id);
|
||||||
let opt = tcx.sess.opts.unstable_opts.always_encode_mir
|
let mut opt = tcx.sess.opts.unstable_opts.always_encode_mir
|
||||||
|| (tcx.sess.opts.output_types.should_codegen()
|
|| (tcx.sess.opts.output_types.should_codegen()
|
||||||
&& reachable_set.contains(&def_id)
|
&& reachable_set.contains(&def_id)
|
||||||
&& (generics.requires_monomorphization(tcx)
|
&& (generics.requires_monomorphization(tcx)
|
||||||
|| tcx.cross_crate_inlinable(def_id)));
|
|| tcx.cross_crate_inlinable(def_id)));
|
||||||
|
if let Some(intrinsic) = tcx.intrinsic(def_id) {
|
||||||
|
opt &= !intrinsic.must_be_overridden;
|
||||||
|
}
|
||||||
// The function has a `const` modifier or is in a `#[const_trait]`.
|
// The function has a `const` modifier or is in a `#[const_trait]`.
|
||||||
let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id())
|
let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id())
|
||||||
|| tcx.is_const_default_method(def_id.to_def_id());
|
|| tcx.is_const_default_method(def_id.to_def_id());
|
||||||
@ -1409,9 +1412,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||||||
if let DefKind::Fn | DefKind::AssocFn = def_kind {
|
if let DefKind::Fn | DefKind::AssocFn = def_kind {
|
||||||
self.tables.asyncness.set_some(def_id.index, tcx.asyncness(def_id));
|
self.tables.asyncness.set_some(def_id.index, tcx.asyncness(def_id));
|
||||||
record_array!(self.tables.fn_arg_names[def_id] <- tcx.fn_arg_names(def_id));
|
record_array!(self.tables.fn_arg_names[def_id] <- tcx.fn_arg_names(def_id));
|
||||||
if let Some(name) = tcx.intrinsic(def_id) {
|
}
|
||||||
record!(self.tables.intrinsic[def_id] <- name);
|
if let Some(name) = tcx.intrinsic(def_id) {
|
||||||
}
|
record!(self.tables.intrinsic[def_id] <- name);
|
||||||
}
|
}
|
||||||
if let DefKind::TyParam = def_kind {
|
if let DefKind::TyParam = def_kind {
|
||||||
let default = self.tcx.object_lifetime_default(def_id);
|
let default = self.tcx.object_lifetime_default(def_id);
|
||||||
|
@ -375,7 +375,7 @@ macro_rules! define_tables {
|
|||||||
|
|
||||||
define_tables! {
|
define_tables! {
|
||||||
- defaulted:
|
- defaulted:
|
||||||
intrinsic: Table<DefIndex, Option<LazyValue<Symbol>>>,
|
intrinsic: Table<DefIndex, Option<LazyValue<ty::IntrinsicDef>>>,
|
||||||
is_macro_rules: Table<DefIndex, bool>,
|
is_macro_rules: Table<DefIndex, bool>,
|
||||||
is_type_alias_impl_trait: Table<DefIndex, bool>,
|
is_type_alias_impl_trait: Table<DefIndex, bool>,
|
||||||
type_alias_is_lazy: Table<DefIndex, bool>,
|
type_alias_is_lazy: Table<DefIndex, bool>,
|
||||||
|
@ -241,7 +241,7 @@ trivial! {
|
|||||||
Option<rustc_target::abi::FieldIdx>,
|
Option<rustc_target::abi::FieldIdx>,
|
||||||
Option<rustc_target::spec::PanicStrategy>,
|
Option<rustc_target::spec::PanicStrategy>,
|
||||||
Option<usize>,
|
Option<usize>,
|
||||||
Option<rustc_span::Symbol>,
|
Option<rustc_middle::ty::IntrinsicDef>,
|
||||||
Result<(), rustc_errors::ErrorGuaranteed>,
|
Result<(), rustc_errors::ErrorGuaranteed>,
|
||||||
Result<(), rustc_middle::traits::query::NoSolution>,
|
Result<(), rustc_middle::traits::query::NoSolution>,
|
||||||
Result<rustc_middle::traits::EvaluationResult, rustc_middle::traits::OverflowError>,
|
Result<rustc_middle::traits::EvaluationResult, rustc_middle::traits::OverflowError>,
|
||||||
|
@ -1760,7 +1760,7 @@ rustc_queries! {
|
|||||||
separate_provide_extern
|
separate_provide_extern
|
||||||
}
|
}
|
||||||
/// Whether the function is an intrinsic
|
/// Whether the function is an intrinsic
|
||||||
query intrinsic(def_id: DefId) -> Option<Symbol> {
|
query intrinsic(def_id: DefId) -> Option<rustc_middle::ty::IntrinsicDef> {
|
||||||
desc { |tcx| "fetch intrinsic name if `{}` is an intrinsic", tcx.def_path_str(def_id) }
|
desc { |tcx| "fetch intrinsic name if `{}` is an intrinsic", tcx.def_path_str(def_id) }
|
||||||
separate_provide_extern
|
separate_provide_extern
|
||||||
}
|
}
|
||||||
|
17
compiler/rustc_middle/src/ty/intrinsic.rs
Normal file
17
compiler/rustc_middle/src/ty/intrinsic.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use rustc_span::{def_id::DefId, Symbol};
|
||||||
|
|
||||||
|
use super::TyCtxt;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Decodable, Encodable, HashStable)]
|
||||||
|
pub struct IntrinsicDef {
|
||||||
|
pub name: Symbol,
|
||||||
|
/// Whether the intrinsic has no meaningful body and all backends need to shim all calls to it.
|
||||||
|
pub must_be_overridden: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TyCtxt<'_> {
|
||||||
|
pub fn is_intrinsic(self, def_id: DefId, name: Symbol) -> bool {
|
||||||
|
let Some(i) = self.intrinsic(def_id) else { return false };
|
||||||
|
i.name == name
|
||||||
|
}
|
||||||
|
}
|
@ -30,6 +30,7 @@ pub use adt::*;
|
|||||||
pub use assoc::*;
|
pub use assoc::*;
|
||||||
pub use generic_args::*;
|
pub use generic_args::*;
|
||||||
pub use generics::*;
|
pub use generics::*;
|
||||||
|
pub use intrinsic::IntrinsicDef;
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
use rustc_ast::node_id::NodeMap;
|
use rustc_ast::node_id::NodeMap;
|
||||||
pub use rustc_ast_ir::{Movability, Mutability};
|
pub use rustc_ast_ir::{Movability, Mutability};
|
||||||
@ -149,6 +150,7 @@ mod generic_args;
|
|||||||
mod generics;
|
mod generics;
|
||||||
mod impls_ty;
|
mod impls_ty;
|
||||||
mod instance;
|
mod instance;
|
||||||
|
mod intrinsic;
|
||||||
mod list;
|
mod list;
|
||||||
mod opaque_types;
|
mod opaque_types;
|
||||||
mod parameterized;
|
mod parameterized;
|
||||||
|
@ -75,6 +75,7 @@ trivially_parameterized_over_tcx! {
|
|||||||
ty::Visibility<DefIndex>,
|
ty::Visibility<DefIndex>,
|
||||||
ty::adjustment::CoerceUnsizedInfo,
|
ty::adjustment::CoerceUnsizedInfo,
|
||||||
ty::fast_reject::SimplifiedType,
|
ty::fast_reject::SimplifiedType,
|
||||||
|
ty::IntrinsicDef,
|
||||||
rustc_ast::Attribute,
|
rustc_ast::Attribute,
|
||||||
rustc_ast::DelimArgs,
|
rustc_ast::DelimArgs,
|
||||||
rustc_ast::expand::StrippedCfgItem<rustc_hir::def_id::DefIndex>,
|
rustc_ast::expand::StrippedCfgItem<rustc_hir::def_id::DefIndex>,
|
||||||
|
@ -19,7 +19,7 @@ use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
|
|||||||
use rustc_index::bit_set::GrowableBitSet;
|
use rustc_index::bit_set::GrowableBitSet;
|
||||||
use rustc_macros::HashStable;
|
use rustc_macros::HashStable;
|
||||||
use rustc_session::Limit;
|
use rustc_session::Limit;
|
||||||
use rustc_span::{sym, Symbol};
|
use rustc_span::sym;
|
||||||
use rustc_target::abi::{Integer, IntegerType, Primitive, Size};
|
use rustc_target::abi::{Integer, IntegerType, Primitive, Size};
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
@ -1641,12 +1641,19 @@ pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
|||||||
.any(|items| items.iter().any(|item| item.has_name(sym::notable_trait)))
|
.any(|items| items.iter().any(|item| item.has_name(sym::notable_trait)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether an item is an intrinsic by Abi. or by whether it has a `rustc_intrinsic` attribute
|
/// Determines whether an item is an intrinsic (which may be via Abi or via the `rustc_intrinsic` attribute)
|
||||||
pub fn intrinsic(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Symbol> {
|
pub fn intrinsic(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::IntrinsicDef> {
|
||||||
|
match tcx.def_kind(def_id) {
|
||||||
|
DefKind::Fn | DefKind::AssocFn => {}
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
if matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic)
|
if matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic)
|
||||||
|| tcx.has_attr(def_id, sym::rustc_intrinsic)
|
|| tcx.has_attr(def_id, sym::rustc_intrinsic)
|
||||||
{
|
{
|
||||||
Some(tcx.item_name(def_id.into()))
|
Some(ty::IntrinsicDef {
|
||||||
|
name: tcx.item_name(def_id.into()),
|
||||||
|
must_be_overridden: tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden),
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -202,7 +202,7 @@ impl PeekCall {
|
|||||||
&terminator.kind
|
&terminator.kind
|
||||||
{
|
{
|
||||||
if let ty::FnDef(def_id, fn_args) = *func.const_.ty().kind() {
|
if let ty::FnDef(def_id, fn_args) = *func.const_.ty().kind() {
|
||||||
if tcx.intrinsic(def_id)? != sym::rustc_peek {
|
if tcx.intrinsic(def_id)?.name != sym::rustc_peek {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,10 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// This just reproduces the logic from Instance::requires_inline.
|
// This just reproduces the logic from Instance::requires_inline.
|
||||||
match tcx.def_kind(def_id) {
|
match tcx.def_kind(def_id) {
|
||||||
DefKind::Ctor(..) | DefKind::Closure => return true,
|
DefKind::Ctor(..) | DefKind::Closure => return true,
|
||||||
|
@ -323,8 +323,8 @@ fn resolve_rust_intrinsic<'tcx>(
|
|||||||
func_ty: Ty<'tcx>,
|
func_ty: Ty<'tcx>,
|
||||||
) -> Option<(Symbol, GenericArgsRef<'tcx>)> {
|
) -> Option<(Symbol, GenericArgsRef<'tcx>)> {
|
||||||
if let ty::FnDef(def_id, args) = *func_ty.kind() {
|
if let ty::FnDef(def_id, args) = *func_ty.kind() {
|
||||||
let name = tcx.intrinsic(def_id)?;
|
let intrinsic = tcx.intrinsic(def_id)?;
|
||||||
return Some((name, args));
|
return Some((intrinsic.name, args));
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,7 @@ fn remap_mir_for_const_eval_select<'tcx>(
|
|||||||
fn_span,
|
fn_span,
|
||||||
..
|
..
|
||||||
} if let ty::FnDef(def_id, _) = *const_.ty().kind()
|
} if let ty::FnDef(def_id, _) = *const_.ty().kind()
|
||||||
&& matches!(tcx.intrinsic(def_id), Some(sym::const_eval_select)) =>
|
&& tcx.is_intrinsic(def_id, sym::const_eval_select) =>
|
||||||
{
|
{
|
||||||
let [tupled_args, called_in_const, called_at_rt]: [_; 3] =
|
let [tupled_args, called_in_const, called_at_rt]: [_; 3] =
|
||||||
std::mem::take(args).try_into().unwrap();
|
std::mem::take(args).try_into().unwrap();
|
||||||
@ -632,6 +632,12 @@ fn optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> &Body<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
|
fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
|
||||||
|
if tcx.intrinsic(did).is_some_and(|i| i.must_be_overridden) {
|
||||||
|
span_bug!(
|
||||||
|
tcx.def_span(did),
|
||||||
|
"this intrinsic must be overridden by the codegen backend, it has no meaningful body",
|
||||||
|
)
|
||||||
|
}
|
||||||
if tcx.is_constructor(did.to_def_id()) {
|
if tcx.is_constructor(did.to_def_id()) {
|
||||||
// There's no reason to run all of the MIR passes on constructors when
|
// There's no reason to run all of the MIR passes on constructors when
|
||||||
// we can just output the MIR we want directly. This also saves const
|
// we can just output the MIR we want directly. This also saves const
|
||||||
|
@ -14,9 +14,9 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
|
|||||||
if let TerminatorKind::Call { func, args, destination, target, .. } =
|
if let TerminatorKind::Call { func, args, destination, target, .. } =
|
||||||
&mut terminator.kind
|
&mut terminator.kind
|
||||||
&& let ty::FnDef(def_id, generic_args) = *func.ty(local_decls, tcx).kind()
|
&& let ty::FnDef(def_id, generic_args) = *func.ty(local_decls, tcx).kind()
|
||||||
&& let Some(intrinsic_name) = tcx.intrinsic(def_id)
|
&& let Some(intrinsic) = tcx.intrinsic(def_id)
|
||||||
{
|
{
|
||||||
match intrinsic_name {
|
match intrinsic.name {
|
||||||
sym::unreachable => {
|
sym::unreachable => {
|
||||||
terminator.kind = TerminatorKind::Unreachable;
|
terminator.kind = TerminatorKind::Unreachable;
|
||||||
}
|
}
|
||||||
@ -105,7 +105,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
|
|||||||
lhs = args.next().unwrap();
|
lhs = args.next().unwrap();
|
||||||
rhs = args.next().unwrap();
|
rhs = args.next().unwrap();
|
||||||
}
|
}
|
||||||
let bin_op = match intrinsic_name {
|
let bin_op = match intrinsic.name {
|
||||||
sym::wrapping_add => BinOp::Add,
|
sym::wrapping_add => BinOp::Add,
|
||||||
sym::wrapping_sub => BinOp::Sub,
|
sym::wrapping_sub => BinOp::Sub,
|
||||||
sym::wrapping_mul => BinOp::Mul,
|
sym::wrapping_mul => BinOp::Mul,
|
||||||
@ -136,7 +136,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
|
|||||||
lhs = args.next().unwrap();
|
lhs = args.next().unwrap();
|
||||||
rhs = args.next().unwrap();
|
rhs = args.next().unwrap();
|
||||||
}
|
}
|
||||||
let bin_op = match intrinsic_name {
|
let bin_op = match intrinsic.name {
|
||||||
sym::add_with_overflow => BinOp::Add,
|
sym::add_with_overflow => BinOp::Add,
|
||||||
sym::sub_with_overflow => BinOp::Sub,
|
sym::sub_with_overflow => BinOp::Sub,
|
||||||
sym::mul_with_overflow => BinOp::Mul,
|
sym::mul_with_overflow => BinOp::Mul,
|
||||||
@ -155,7 +155,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
|
|||||||
sym::size_of | sym::min_align_of => {
|
sym::size_of | sym::min_align_of => {
|
||||||
if let Some(target) = *target {
|
if let Some(target) = *target {
|
||||||
let tp_ty = generic_args.type_at(0);
|
let tp_ty = generic_args.type_at(0);
|
||||||
let null_op = match intrinsic_name {
|
let null_op = match intrinsic.name {
|
||||||
sym::size_of => NullOp::SizeOf,
|
sym::size_of => NullOp::SizeOf,
|
||||||
sym::min_align_of => NullOp::AlignOf,
|
sym::min_align_of => NullOp::AlignOf,
|
||||||
_ => bug!("unexpected intrinsic"),
|
_ => bug!("unexpected intrinsic"),
|
||||||
|
@ -1019,6 +1019,11 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) ->
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) {
|
||||||
|
// These are implemented by backends directly and have no meaningful body.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if def_id.is_local() {
|
if def_id.is_local() {
|
||||||
// Local items cannot be referred to locally without monomorphizing them locally.
|
// Local items cannot be referred to locally without monomorphizing them locally.
|
||||||
return true;
|
return true;
|
||||||
|
@ -1525,6 +1525,7 @@ symbols! {
|
|||||||
rustc_inherit_overflow_checks,
|
rustc_inherit_overflow_checks,
|
||||||
rustc_insignificant_dtor,
|
rustc_insignificant_dtor,
|
||||||
rustc_intrinsic,
|
rustc_intrinsic,
|
||||||
|
rustc_intrinsic_must_be_overridden,
|
||||||
rustc_layout,
|
rustc_layout,
|
||||||
rustc_layout_scalar_valid_range_end,
|
rustc_layout_scalar_valid_range_end,
|
||||||
rustc_layout_scalar_valid_range_start,
|
rustc_layout_scalar_valid_range_start,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use rustc_errors::ErrorGuaranteed;
|
use rustc_errors::ErrorGuaranteed;
|
||||||
use rustc_hir::def::DefKind;
|
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_middle::query::Providers;
|
use rustc_middle::query::Providers;
|
||||||
@ -28,8 +27,7 @@ fn resolve_instance<'tcx>(
|
|||||||
tcx.normalize_erasing_regions(param_env, args),
|
tcx.normalize_erasing_regions(param_env, args),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let def = if matches!(tcx.def_kind(def_id), DefKind::Fn) && tcx.intrinsic(def_id).is_some()
|
let def = if tcx.intrinsic(def_id).is_some() {
|
||||||
{
|
|
||||||
debug!(" => intrinsic");
|
debug!(" => intrinsic");
|
||||||
ty::InstanceDef::Intrinsic(def_id)
|
ty::InstanceDef::Intrinsic(def_id)
|
||||||
} else if Some(def_id) == tcx.lang_items().drop_in_place_fn() {
|
} else if Some(def_id) == tcx.lang_items().drop_in_place_fn() {
|
||||||
|
@ -2499,9 +2499,8 @@ extern "rust-intrinsic" {
|
|||||||
#[rustc_nounwind]
|
#[rustc_nounwind]
|
||||||
pub fn black_box<T>(dummy: T) -> T;
|
pub fn black_box<T>(dummy: T) -> T;
|
||||||
|
|
||||||
/// `ptr` must point to a vtable.
|
|
||||||
/// The intrinsic will return the size stored in that vtable.
|
|
||||||
#[rustc_nounwind]
|
#[rustc_nounwind]
|
||||||
|
#[cfg(bootstrap)]
|
||||||
pub fn vtable_size(ptr: *const ()) -> usize;
|
pub fn vtable_size(ptr: *const ()) -> usize;
|
||||||
|
|
||||||
/// `ptr` must point to a vtable.
|
/// `ptr` must point to a vtable.
|
||||||
@ -2681,6 +2680,17 @@ pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 {
|
|||||||
#[cfg_attr(bootstrap, inline)]
|
#[cfg_attr(bootstrap, inline)]
|
||||||
pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
|
pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
|
||||||
|
|
||||||
|
/// `ptr` must point to a vtable.
|
||||||
|
/// The intrinsic will return the size stored in that vtable.
|
||||||
|
#[rustc_nounwind]
|
||||||
|
#[unstable(feature = "core_intrinsics", issue = "none")]
|
||||||
|
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
|
||||||
|
#[cfg_attr(not(bootstrap), rustc_intrinsic_must_be_overridden)]
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
// Some functions are defined here because they accidentally got made
|
// Some functions are defined here because they accidentally got made
|
||||||
// available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
|
// available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
|
||||||
// (`transmute` also falls into this category, but it cannot be wrapped due to the
|
// (`transmute` also falls into this category, but it cannot be wrapped due to the
|
||||||
|
@ -52,12 +52,23 @@ with any regular function.
|
|||||||
Various intrinsics have native MIR operations that they correspond to. Instead of requiring
|
Various intrinsics have native MIR operations that they correspond to. Instead of requiring
|
||||||
backends to implement both the intrinsic and the MIR operation, the `lower_intrinsics` pass
|
backends to implement both the intrinsic and the MIR operation, the `lower_intrinsics` pass
|
||||||
will convert the calls to the MIR operation. Backends do not need to know about these intrinsics
|
will convert the calls to the MIR operation. Backends do not need to know about these intrinsics
|
||||||
at all.
|
at all. These intrinsics only make sense without a body, and can either be declared as a "rust-intrinsic"
|
||||||
|
or as a `#[rustc_intrinsic]`. The body is never used, as calls to the intrinsic do not exist
|
||||||
|
anymore after MIR analyses.
|
||||||
|
|
||||||
## Intrinsics without fallback logic
|
## Intrinsics without fallback logic
|
||||||
|
|
||||||
These must be implemented by all backends.
|
These must be implemented by all backends.
|
||||||
|
|
||||||
|
### `#[rustc_intrinsic]` declarations
|
||||||
|
|
||||||
|
These are written like intrinsics with fallback bodies, but the body is irrelevant.
|
||||||
|
Use `loop {}` for the body or call the intrinsic recursively and add
|
||||||
|
`#[rustc_intrinsic_must_be_overridden]` to the function to ensure that backends don't
|
||||||
|
invoke the body.
|
||||||
|
|
||||||
|
### Legacy extern ABI based intrinsics
|
||||||
|
|
||||||
These are imported as if they were FFI functions, with the special
|
These are imported as if they were FFI functions, with the special
|
||||||
`rust-intrinsic` ABI. For example, if one was in a freestanding
|
`rust-intrinsic` ABI. For example, if one was in a freestanding
|
||||||
context, but wished to be able to `transmute` between types, and
|
context, but wished to be able to `transmute` between types, and
|
||||||
|
@ -334,7 +334,7 @@ fn check_terminator<'tcx>(
|
|||||||
// within const fns. `transmute` is allowed in all other const contexts.
|
// within const fns. `transmute` is allowed in all other const contexts.
|
||||||
// This won't really scale to more intrinsics or functions. Let's allow const
|
// This won't really scale to more intrinsics or functions. Let's allow const
|
||||||
// transmutes in const fn before we add more hacks to this.
|
// transmutes in const fn before we add more hacks to this.
|
||||||
if matches!(tcx.intrinsic(fn_def_id), Some(sym::transmute)) {
|
if tcx.is_intrinsic(fn_def_id, sym::transmute) {
|
||||||
return Err((
|
return Err((
|
||||||
span,
|
span,
|
||||||
"can only call `transmute` from const items, not `const fn`".into(),
|
"can only call `transmute` from const items, not `const fn`".into(),
|
||||||
|
20
tests/ui/intrinsics/always-gets-overridden.rs
Normal file
20
tests/ui/intrinsics/always-gets-overridden.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//! Check that `vtable_size` gets overridden by llvm backend even if there is no
|
||||||
|
//! `rustc_intrinsic_must_be_overridden` attribute on this usage.
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
//@run-pass
|
||||||
|
|
||||||
|
#[rustc_intrinsic]
|
||||||
|
pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Trait {}
|
||||||
|
impl Trait for () {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x: &dyn Trait = &();
|
||||||
|
unsafe {
|
||||||
|
let (_data, vtable): (*const (), *const ()) = core::mem::transmute(x);
|
||||||
|
assert_eq!(vtable_size(vtable), 0);
|
||||||
|
}
|
||||||
|
}
|
18
tests/ui/intrinsics/not-overridden.rs
Normal file
18
tests/ui/intrinsics/not-overridden.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//! Check that intrinsics that do not get overridden, but are marked as such,
|
||||||
|
//! cause an error instead of silently invoking the body.
|
||||||
|
#![feature(rustc_attrs, effects)]
|
||||||
|
//@ build-fail
|
||||||
|
//@ failure-status:101
|
||||||
|
//@ normalize-stderr-test ".*note: .*\n\n" -> ""
|
||||||
|
//@ normalize-stderr-test "thread 'rustc' panicked.*:\n.*\n" -> ""
|
||||||
|
//@ normalize-stderr-test "internal compiler error:.*: intrinsic const_deallocate " -> ""
|
||||||
|
//@ rustc-env:RUST_BACKTRACE=0
|
||||||
|
|
||||||
|
#[rustc_intrinsic]
|
||||||
|
#[rustc_intrinsic_must_be_overridden]
|
||||||
|
pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
unsafe { const_deallocate(std::ptr::null_mut(), 0, 0) }
|
||||||
|
//~^ ERROR: must be overridden
|
||||||
|
}
|
10
tests/ui/intrinsics/not-overridden.stderr
Normal file
10
tests/ui/intrinsics/not-overridden.stderr
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
error: must be overridden by codegen backend, but isn't
|
||||||
|
--> $DIR/not-overridden.rs:16:14
|
||||||
|
|
|
||||||
|
LL | unsafe { const_deallocate(std::ptr::null_mut(), 0, 0) }
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
query stack during panic:
|
||||||
|
end of query stack
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user