Skip to content

Commit bcb8565

Browse files
pnkfelixcelinval
authored andcommitted
Contracts core intrinsics.
These are hooks to: 1. control whether contract checks are run 2. allow 3rd party tools to intercept and reintepret the results of running contracts.
1 parent 534d79a commit bcb8565

File tree

30 files changed

+183
-6
lines changed

30 files changed

+183
-6
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,6 +1650,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
16501650
ConstraintCategory::SizedBound,
16511651
);
16521652
}
1653+
&Rvalue::NullaryOp(NullOp::ContractChecks, _) => {}
16531654
&Rvalue::NullaryOp(NullOp::UbChecks, _) => {}
16541655

16551656
Rvalue::ShallowInitBox(operand, ty) => {

compiler/rustc_codegen_cranelift/src/base.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,15 @@ fn codegen_stmt<'tcx>(
874874
lval.write_cvalue(fx, val);
875875
return;
876876
}
877+
NullOp::ContractChecks => {
878+
let val = fx.tcx.sess.contract_checks();
879+
let val = CValue::by_val(
880+
fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()),
881+
fx.layout_of(fx.tcx.types.bool),
882+
);
883+
lval.write_cvalue(fx, val);
884+
return;
885+
}
877886
};
878887
let val = CValue::by_val(
879888
fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(val).unwrap()),

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
741741
let val = bx.tcx().sess.ub_checks();
742742
bx.cx().const_bool(val)
743743
}
744+
mir::NullOp::ContractChecks => {
745+
let val = bx.tcx().sess.contract_checks();
746+
bx.cx().const_bool(val)
747+
}
744748
};
745749
let tcx = self.cx.tcx();
746750
OperandRef {

compiler/rustc_const_eval/src/check_consts/check.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
675675
Rvalue::Cast(_, _, _) => {}
676676

677677
Rvalue::NullaryOp(
678-
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks,
678+
NullOp::SizeOf
679+
| NullOp::AlignOf
680+
| NullOp::OffsetOf(_)
681+
| NullOp::UbChecks
682+
| NullOp::ContractChecks,
679683
_,
680684
) => {}
681685
Rvalue::ShallowInitBox(_, _) => {}

compiler/rustc_const_eval/src/interpret/machine.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,9 @@ pub trait Machine<'tcx>: Sized {
293293
/// Determines the result of a `NullaryOp::UbChecks` invocation.
294294
fn ub_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>;
295295

296+
/// Determines the result of a `NullaryOp::ContractChecks` invocation.
297+
fn contract_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>;
298+
296299
/// Called when the interpreter encounters a `StatementKind::ConstEvalCounter` instruction.
297300
/// You can use this to detect long or endlessly running programs.
298301
#[inline]
@@ -679,6 +682,13 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
679682
interp_ok(true)
680683
}
681684

685+
#[inline(always)]
686+
fn contract_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> {
687+
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to
688+
// unsound differences in evaluating the same constant at different instantiation sites.
689+
interp_ok(true)
690+
}
691+
682692
#[inline(always)]
683693
fn adjust_global_allocation<'b>(
684694
_ecx: &InterpCx<$tcx, Self>,

compiler/rustc_const_eval/src/interpret/operator.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
537537
ImmTy::from_uint(val, usize_layout())
538538
}
539539
UbChecks => ImmTy::from_bool(M::ub_checks(self)?, *self.tcx),
540+
ContractChecks => ImmTy::from_bool(M::contract_checks(self)?, *self.tcx),
540541
})
541542
}
542543
}

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const GATED_CFGS: &[GatedCfg] = &[
1919
// (name in cfg, feature, function to check if the feature is enabled)
2020
(sym::overflow_checks, sym::cfg_overflow_checks, Features::cfg_overflow_checks),
2121
(sym::ub_checks, sym::cfg_ub_checks, Features::cfg_ub_checks),
22+
(sym::contract_checks, sym::cfg_contract_checks, Features::cfg_contract_checks),
2223
(sym::target_thread_local, sym::cfg_target_thread_local, Features::cfg_target_thread_local),
2324
(
2425
sym::target_has_atomic_equal_alignment,

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,8 @@ declare_features! (
403403
(unstable, c_variadic, "1.34.0", Some(44930)),
404404
/// Allows the use of `#[cfg(<true/false>)]`.
405405
(unstable, cfg_boolean_literals, "1.83.0", Some(131204)),
406+
/// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled.
407+
(unstable, cfg_contract_checks, "CURRENT_RUSTC_VERSION", Some(133866)),
406408
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
407409
(unstable, cfg_overflow_checks, "1.71.0", Some(111466)),
408410
/// Provides the relocation model information as cfg entry

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
132132
| sym::aggregate_raw_ptr
133133
| sym::ptr_metadata
134134
| sym::ub_checks
135+
| sym::contract_checks
136+
| sym::contract_check_requires
137+
| sym::contract_check_ensures
135138
| sym::fadd_algebraic
136139
| sym::fsub_algebraic
137140
| sym::fmul_algebraic
@@ -219,6 +222,18 @@ pub fn check_intrinsic_type(
219222
}
220223
};
221224
(n_tps, 0, 0, inputs, output, hir::Safety::Unsafe)
225+
} else if intrinsic_name == sym::contract_check_ensures {
226+
// contract_check_ensures::<'a, Ret, C>(&'a Ret, C) -> bool
227+
// where C: impl Fn(&'a Ret) -> bool,
228+
//
229+
// so: two type params, one lifetime param, 0 const params, two inputs, returns boolean
230+
231+
let p = generics.param_at(0, tcx);
232+
let r = ty::Region::new_early_param(tcx, p.to_early_bound_region_data());
233+
let ref_ret = Ty::new_imm_ref(tcx, r, param(1));
234+
// let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon };
235+
// let ref_ret = Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0));
236+
(2, 1, 0, vec![ref_ret, param(2)], tcx.types.bool, hir::Safety::Safe)
222237
} else {
223238
let safety = intrinsic_operation_unsafety(tcx, intrinsic_id);
224239
let (n_tps, n_cts, inputs, output) = match intrinsic_name {
@@ -610,6 +625,11 @@ pub fn check_intrinsic_type(
610625

611626
sym::box_new => (1, 0, vec![param(0)], Ty::new_box(tcx, param(0))),
612627

628+
// contract_checks() -> bool
629+
sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool),
630+
// contract_check_requires::<C>(C) -> bool, where C: impl Fn() -> bool
631+
sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.bool),
632+
613633
sym::simd_eq
614634
| sym::simd_ne
615635
| sym::simd_lt

compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
11031103
NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
11041104
NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
11051105
NullOp::UbChecks => write!(fmt, "UbChecks()"),
1106+
NullOp::ContractChecks => write!(fmt, "ContractChecks()"),
11061107
}
11071108
}
11081109
ThreadLocalRef(did) => ty::tls::with(|tcx| {

0 commit comments

Comments
 (0)