Skip to content
Closed
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
7 changes: 6 additions & 1 deletion compiler/rustc_abi/src/canon_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub enum CanonAbi {
Rust,
RustCold,
RustPreserveNone,
RustTail,

/// An ABI that rustc does not know how to call or define.
Custom,
Expand Down Expand Up @@ -59,7 +60,10 @@ pub enum CanonAbi {
impl CanonAbi {
pub fn is_rustic_abi(self) -> bool {
match self {
CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone => true,
CanonAbi::Rust
| CanonAbi::RustCold
| CanonAbi::RustPreserveNone
| CanonAbi::RustTail => true,
CanonAbi::C
| CanonAbi::Custom
| CanonAbi::Swift
Expand All @@ -81,6 +85,7 @@ impl fmt::Display for CanonAbi {
CanonAbi::Rust => ExternAbi::Rust,
CanonAbi::RustCold => ExternAbi::RustCold,
CanonAbi::RustPreserveNone => ExternAbi::RustPreserveNone,
CanonAbi::RustTail => ExternAbi::RustTail,
CanonAbi::Custom => ExternAbi::Custom,
CanonAbi::Swift => ExternAbi::Swift,
CanonAbi::Arm(arm_call) => match arm_call {
Expand Down
9 changes: 8 additions & 1 deletion compiler/rustc_abi/src/extern_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ pub enum ExternAbi {
/// forcing callers to save all registers.
RustPreserveNone,

/// Ensures that calls in tail position can always be optimized into a jump.
///
/// This ABI is not stable, and relies on LLVM implementation details.
RustTail,

/// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM.
/// Even normally-compatible Rust types can become ABI-incompatible with this ABI!
Unadjusted,
Expand Down Expand Up @@ -205,6 +210,7 @@ abi_impls! {
System { unwind: true } =><= "system-unwind",
SysV64 { unwind: false } =><= "sysv64",
SysV64 { unwind: true } =><= "sysv64-unwind",
RustTail =><= "tail",
Thiscall { unwind: false } =><= "thiscall",
Thiscall { unwind: true } =><= "thiscall-unwind",
Unadjusted =><= "unadjusted",
Expand Down Expand Up @@ -280,7 +286,7 @@ impl ExternAbi {
/// - are subject to change between compiler versions
pub fn is_rustic_abi(self) -> bool {
use ExternAbi::*;
matches!(self, Rust | RustCall | RustCold | RustPreserveNone)
matches!(self, Rust | RustCall | RustCold | RustPreserveNone | RustTail)
}

/// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*
Expand Down Expand Up @@ -354,6 +360,7 @@ impl ExternAbi {
| Self::SysV64 { .. }
| Self::Win64 { .. }
| Self::RustPreserveNone
| Self::RustTail
| Self::Swift => true,
}
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_ast_lowering/src/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
feature: sym::rust_preserve_none_cc,
explain: GateReason::Experimental,
}),
ExternAbi::RustTail => {
Err(UnstableAbi { abi, feature: sym::rust_tail_cc, explain: GateReason::Experimental })
}
ExternAbi::RustInvalid => {
Err(UnstableAbi { abi, feature: sym::rustc_attrs, explain: GateReason::ImplDetail })
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ impl<'a> AstValidator<'a> {
| CanonAbi::Rust
| CanonAbi::RustCold
| CanonAbi::RustPreserveNone
| CanonAbi::RustTail
| CanonAbi::Swift
| CanonAbi::Arm(_)
| CanonAbi::X86(_) => { /* nothing to check */ }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::SubregionOrigin;
use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::canonical::{QueryRegionConstraint, QueryRegionConstraints};
use rustc_infer::infer::outlives::env::RegionBoundPairs;
use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate};
use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound};
Expand Down Expand Up @@ -74,9 +74,9 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
let assumptions =
elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied());

for &(constraint, constraint_category, _) in constraints {
for &QueryRegionConstraint { constraint, category, .. } in constraints {
constraint.iter_outlives().for_each(|predicate| {
self.convert(predicate, constraint_category, &assumptions);
self.convert(predicate, category, &assumptions);
});
}
}
Expand Down Expand Up @@ -296,7 +296,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
// FIXME(higher_ranked_auto): What should we do with the assumptions here?
if let Some(QueryRegionConstraints { constraints, assumptions: _ }) = constraints {
next_outlives_predicates.extend(constraints.iter().flat_map(
|(constraint, category, _)| {
|QueryRegionConstraint { constraint, category, .. }| {
constraint.iter_outlives().map(|outlives| (outlives, *category))
},
));
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_codegen_cranelift/src/abi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ pub(crate) fn conv_to_call_conv(
match c {
CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::C => default_call_conv,

// Cranelift doesn't currently have anything for this.
CanonAbi::RustPreserveNone => default_call_conv,
CanonAbi::RustPreserveNone | CanonAbi::RustTail => {
sess.dcx().fatal(format!("call conv {c:?} is LLVM-specific"))
}

// Functions with this calling convention can only be called from assembly, but it is
// possible to declare an `extern "custom"` block, so the backend still needs a calling
Expand All @@ -71,7 +72,7 @@ pub(crate) fn conv_to_call_conv(
},

CanonAbi::Interrupt(_) | CanonAbi::Arm(_) | CanonAbi::Swift => {
sess.dcx().fatal("call conv {c:?} is not yet implemented")
sess.dcx().fatal(format!("call conv {c:?} is not yet implemented"))
}
CanonAbi::GpuKernel => {
unreachable!("tried to use {c:?} call conv which only exists on an unsupported target")
Expand Down
29 changes: 20 additions & 9 deletions compiler/rustc_codegen_gcc/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_middle::bug;
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::LayoutOf;
#[cfg(feature = "master")]
use rustc_session::config;
use rustc_session::{Session, config};
use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode};
#[cfg(feature = "master")]
use rustc_target::spec::Arch;
Expand Down Expand Up @@ -230,32 +230,43 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {

#[cfg(feature = "master")]
fn gcc_cconv(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Option<FnAttribute<'gcc>> {
conv_to_fn_attribute(self.conv, &cx.tcx.sess.target.arch)
conv_to_fn_attribute(cx.sess(), self.conv)
}
}

#[cfg(feature = "master")]
pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &Arch) -> Option<FnAttribute<'gcc>> {
pub fn conv_to_fn_attribute<'gcc>(sess: &Session, conv: CanonAbi) -> Option<FnAttribute<'gcc>> {
let attribute = match conv {
CanonAbi::C | CanonAbi::Rust => return None,
// gcc/gccjit does not have anything for this.
CanonAbi::RustPreserveNone => return None,
CanonAbi::RustPreserveNone => {
// This calling convention is LLVM-specific and unspecified.
sess.dcx()
.fatal("gcc/gccjit backend does not support RustPreserveNone calling convention")
}
CanonAbi::RustTail => {
// This calling convention is LLVM-specific and unspecified.
sess.dcx().fatal("gcc/gccjit backend does not support RustTail calling convention")
}
CanonAbi::RustCold => FnAttribute::Cold,
// Functions with this calling convention can only be called from assembly, but it is
// possible to declare an `extern "custom"` block, so the backend still needs a calling
// convention for declaring foreign functions.
CanonAbi::Custom => return None,
// gcc/gccjit does not have anything for Swift's calling convention.
CanonAbi::Swift => panic!("gcc/gccjit backend does not support Swift calling convention"),
CanonAbi::Swift => {
// gcc/gccjit does not have anything for Swift's calling convention.
sess.dcx().fatal("gcc/gccjit backend does not support Swift calling convention")
}
CanonAbi::Arm(arm_call) => match arm_call {
ArmCall::CCmseNonSecureCall => FnAttribute::ArmCmseNonsecureCall,
ArmCall::CCmseNonSecureEntry => FnAttribute::ArmCmseNonsecureEntry,
ArmCall::Aapcs => FnAttribute::ArmPcs("aapcs"),
},
CanonAbi::GpuKernel => match arch {
CanonAbi::GpuKernel => match &sess.target.arch {
&Arch::AmdGpu => FnAttribute::GcnAmdGpuHsaKernel,
&Arch::Nvptx64 => FnAttribute::NvptxKernel,
arch => panic!("Arch {arch} does not support GpuKernel calling convention"),
arch => sess
.dcx()
.fatal(format!("Arch {arch} does not support GpuKernel calling convention")),
},
// FIXME(antoyo): check if those AVR attributes are mapped correctly.
CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_codegen_gcc/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,10 +486,10 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
fn declare_c_main(&self, fn_type: Self::Type) -> Option<Self::Function> {
let entry_name = self.sess().target.entry_name.as_ref();
if !self.functions.borrow().contains_key(entry_name) {
#[cfg(feature = "master")]
let conv = conv_to_fn_attribute(self.sess().target.entry_abi, &self.sess().target.arch);
#[cfg(not(feature = "master"))]
let conv = None;
let conv = cfg_select! {
feature = "master" => conv_to_fn_attribute(self.sess(), self.sess().target.entry_abi),
_ => None,
};
Some(self.declare_entry_fn(entry_name, fn_type, conv))
} else {
// If the symbol already exists, it is an error: for example, the user wrote
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_codegen_llvm/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,10 @@ pub(crate) fn to_llvm_calling_convention(sess: &Session, abi: CanonAbi) -> llvm:
Arch::X86_64 | Arch::AArch64 => llvm::PreserveNone,
_ => llvm::CCallConv,
},
CanonAbi::RustTail => match &sess.target.arch {
Arch::X86 | Arch::X86_64 | Arch::AArch64 => llvm::Tail,
_ => sess.dcx().fatal("extern \"tail\" is only supported on x86_64 and aarch64"),
},
// Functions with this calling convention can only be called from assembly, but it is
// possible to declare an `extern "custom"` block, so the backend still needs a calling
// convention for declaring foreign functions.
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,8 @@ declare_features! (
(unstable, rust_cold_cc, "1.63.0", Some(97544)),
/// Allows `extern "rust-preserve-none"`.
(unstable, rust_preserve_none_cc, "1.95.0", Some(151401)),
/// Allows `extern "tail"`.
(unstable, rust_tail_cc, "CURRENT_RUSTC_VERSION", Some(157427)),
/// Target features on s390x.
(unstable, s390x_target_feature, "1.82.0", Some(150259)),
/// Allows the use of the `sanitize` attribute.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| CanonAbi::Rust
| CanonAbi::RustCold
| CanonAbi::RustPreserveNone
| CanonAbi::RustTail
| CanonAbi::Swift
| CanonAbi::Arm(_)
| CanonAbi::X86(_) => {}
Expand Down
19 changes: 18 additions & 1 deletion compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4796,7 +4796,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let hir::Node::Expr(rcvr) = self.tcx.hir_node(hir_id) else {
return false;
};
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, rcvr_ty.into_iter());
// The trait may have generic parameters beyond `Self` (e.g. `Borrow<Borrowed>`), and
// `rcvr_ty` may even be unknown. We only ever know the receiver type (the `Self` arg),
// so fill `Self` from `rcvr_ty` when available and the remaining parameters with fresh
// inference variables; building a `TraitRef` with a partial arg list would otherwise trip
// `debug_assert_args_compatible` and ICE. See #157189.
let trait_ref = ty::TraitRef::new_from_args(
self.tcx,
trait_def_id,
ty::GenericArgs::for_item(self.tcx, trait_def_id, |param, _| {
if param.index == 0
&& let Some(rcvr_ty) = rcvr_ty
{
rcvr_ty.into()
} else {
self.var_for_def(rcvr.span, param)
}
}),
);
let trait_pred = ty::Binder::dummy(ty::TraitPredicate {
trait_ref,
polarity: ty::PredicatePolarity::Positive,
Expand Down
43 changes: 27 additions & 16 deletions compiler/rustc_infer/src/infer/canonical/query_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::iter;
use rustc_index::{Idx, IndexVec};
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::bug;
use rustc_middle::infer::canonical::CanonicalVarKind;
use rustc_middle::infer::canonical::{CanonicalVarKind, QueryRegionConstraint};
use rustc_middle::ty::{self, BoundVar, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable};
use tracing::{debug, instrument};

Expand Down Expand Up @@ -188,7 +188,9 @@ impl<'tcx> InferCtxt<'tcx> {
let InferOk { value: result_args, obligations } =
self.query_response_instantiation(cause, param_env, original_values, query_response)?;

for (constraint, _category, vis) in &query_response.value.region_constraints.constraints {
for QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in
&query_response.value.region_constraints.constraints
{
let constraint = instantiate_value(self.tcx, &result_args, *constraint);
match constraint {
ty::RegionConstraint::Outlives(predicate) => {
Expand Down Expand Up @@ -285,11 +287,12 @@ impl<'tcx> InferCtxt<'tcx> {

(GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => {
if v_o != v_r {
output_query_region_constraints.constraints.push((
ty::RegionEqPredicate(v_o, v_r).into(),
constraint_category,
ty::VisibleForLeakCheck::Yes,
));
let constraint = QueryRegionConstraint {
constraint: ty::RegionEqPredicate(v_o, v_r).into(),
category: constraint_category,
visible_for_leak_check: ty::VisibleForLeakCheck::Yes,
};
output_query_region_constraints.constraints.push(constraint);
}
}

Expand Down Expand Up @@ -321,7 +324,7 @@ impl<'tcx> InferCtxt<'tcx> {
let r_c = instantiate_value(self.tcx, &result_args, r_c);

// Screen out `'a: 'a` or `'a == 'a` cases.
if r_c.0.is_trivial() { None } else { Some(r_c) }
if r_c.constraint.is_trivial() { None } else { Some(r_c) }
}),
);

Expand Down Expand Up @@ -616,7 +619,7 @@ pub fn make_query_region_constraints<'tcx>(

debug!(?constraints);

let constraints: Vec<_> = constraints
let constraints: Vec<QueryRegionConstraint<'tcx>> = constraints
.iter()
.map(|(c, origin)| match c.kind {
ConstraintKind::VarSubVar
Expand All @@ -625,22 +628,30 @@ pub fn make_query_region_constraints<'tcx>(
| ConstraintKind::RegSubReg => {
// Swap regions because we are going from sub (<=) to outlives (>=).
let constraint = ty::OutlivesPredicate(c.sup.into(), c.sub).into();
(constraint, origin.to_constraint_category(), c.visible_for_leak_check)
QueryRegionConstraint {
constraint,
category: origin.to_constraint_category(),
visible_for_leak_check: c.visible_for_leak_check,
}
}

ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
let constraint = ty::RegionEqPredicate(c.sup, c.sub).into();
(constraint, origin.to_constraint_category(), c.visible_for_leak_check)
QueryRegionConstraint {
constraint,
category: origin.to_constraint_category(),
visible_for_leak_check: c.visible_for_leak_check,
}
}
})
.chain(outlives_obligations.into_iter().map(
|TypeOutlivesConstraint { sub_region, sup_type, origin }| {
(
ty::OutlivesPredicate(sup_type.into(), sub_region).into(),
origin.to_constraint_category(),
QueryRegionConstraint {
constraint: ty::OutlivesPredicate(sup_type.into(), sub_region).into(),
category: origin.to_constraint_category(),
// We don't do leak checks for type outlives
ty::VisibleForLeakCheck::Unreachable,
)
visible_for_leak_check: ty::VisibleForLeakCheck::Unreachable,
}
},
))
.collect();
Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_middle/src/infer/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,12 @@ impl<'tcx, R> QueryResponse<'tcx, R> {
}
}

// FIXME: Convert this into a struct
pub type QueryRegionConstraint<'tcx> =
(ty::RegionConstraint<'tcx>, ConstraintCategory<'tcx>, ty::VisibleForLeakCheck);
#[derive(Debug, StableHash, Hash, Eq, PartialEq, TypeVisitable, Clone, TypeFoldable, Copy)]
pub struct QueryRegionConstraint<'tcx> {
pub constraint: ty::RegionConstraint<'tcx>,
pub category: ConstraintCategory<'tcx>,
pub visible_for_leak_check: ty::VisibleForLeakCheck,
}

#[derive(Default)]
pub struct CanonicalParamEnvCache<'tcx> {
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1305,7 +1305,9 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi)
| RustInvalid
| Swift
| Unadjusted => false,
Rust | RustCall | RustCold | RustPreserveNone => tcx.sess.panic_strategy().unwinds(),
Rust | RustCall | RustCold | RustPreserveNone | RustTail => {
tcx.sess.panic_strategy().unwinds()
}
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_public/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ pub enum CallConvention {
PreserveMost,
PreserveAll,
PreserveNone,
Tail,

Custom,

Expand Down
Loading
Loading