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.
This commit is contained in:
parent
534d79adf9
commit
bcb8565f30
30 changed files with 183 additions and 6 deletions
|
@ -1650,6 +1650,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
ConstraintCategory::SizedBound,
|
ConstraintCategory::SizedBound,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
&Rvalue::NullaryOp(NullOp::ContractChecks, _) => {}
|
||||||
&Rvalue::NullaryOp(NullOp::UbChecks, _) => {}
|
&Rvalue::NullaryOp(NullOp::UbChecks, _) => {}
|
||||||
|
|
||||||
Rvalue::ShallowInitBox(operand, ty) => {
|
Rvalue::ShallowInitBox(operand, ty) => {
|
||||||
|
|
|
@ -874,6 +874,15 @@ fn codegen_stmt<'tcx>(
|
||||||
lval.write_cvalue(fx, val);
|
lval.write_cvalue(fx, val);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
NullOp::ContractChecks => {
|
||||||
|
let val = fx.tcx.sess.contract_checks();
|
||||||
|
let val = CValue::by_val(
|
||||||
|
fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()),
|
||||||
|
fx.layout_of(fx.tcx.types.bool),
|
||||||
|
);
|
||||||
|
lval.write_cvalue(fx, val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let val = CValue::by_val(
|
let val = CValue::by_val(
|
||||||
fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(val).unwrap()),
|
fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(val).unwrap()),
|
||||||
|
|
|
@ -741,6 +741,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
let val = bx.tcx().sess.ub_checks();
|
let val = bx.tcx().sess.ub_checks();
|
||||||
bx.cx().const_bool(val)
|
bx.cx().const_bool(val)
|
||||||
}
|
}
|
||||||
|
mir::NullOp::ContractChecks => {
|
||||||
|
let val = bx.tcx().sess.contract_checks();
|
||||||
|
bx.cx().const_bool(val)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let tcx = self.cx.tcx();
|
let tcx = self.cx.tcx();
|
||||||
OperandRef {
|
OperandRef {
|
||||||
|
|
|
@ -675,7 +675,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||||
Rvalue::Cast(_, _, _) => {}
|
Rvalue::Cast(_, _, _) => {}
|
||||||
|
|
||||||
Rvalue::NullaryOp(
|
Rvalue::NullaryOp(
|
||||||
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks,
|
NullOp::SizeOf
|
||||||
|
| NullOp::AlignOf
|
||||||
|
| NullOp::OffsetOf(_)
|
||||||
|
| NullOp::UbChecks
|
||||||
|
| NullOp::ContractChecks,
|
||||||
_,
|
_,
|
||||||
) => {}
|
) => {}
|
||||||
Rvalue::ShallowInitBox(_, _) => {}
|
Rvalue::ShallowInitBox(_, _) => {}
|
||||||
|
|
|
@ -293,6 +293,9 @@ pub trait Machine<'tcx>: Sized {
|
||||||
/// Determines the result of a `NullaryOp::UbChecks` invocation.
|
/// Determines the result of a `NullaryOp::UbChecks` invocation.
|
||||||
fn ub_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>;
|
fn ub_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>;
|
||||||
|
|
||||||
|
/// Determines the result of a `NullaryOp::ContractChecks` invocation.
|
||||||
|
fn contract_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>;
|
||||||
|
|
||||||
/// Called when the interpreter encounters a `StatementKind::ConstEvalCounter` instruction.
|
/// Called when the interpreter encounters a `StatementKind::ConstEvalCounter` instruction.
|
||||||
/// You can use this to detect long or endlessly running programs.
|
/// You can use this to detect long or endlessly running programs.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -679,6 +682,13 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
|
||||||
interp_ok(true)
|
interp_ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn contract_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> {
|
||||||
|
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to
|
||||||
|
// unsound differences in evaluating the same constant at different instantiation sites.
|
||||||
|
interp_ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn adjust_global_allocation<'b>(
|
fn adjust_global_allocation<'b>(
|
||||||
_ecx: &InterpCx<$tcx, Self>,
|
_ecx: &InterpCx<$tcx, Self>,
|
||||||
|
|
|
@ -537,6 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||||
ImmTy::from_uint(val, usize_layout())
|
ImmTy::from_uint(val, usize_layout())
|
||||||
}
|
}
|
||||||
UbChecks => ImmTy::from_bool(M::ub_checks(self)?, *self.tcx),
|
UbChecks => ImmTy::from_bool(M::ub_checks(self)?, *self.tcx),
|
||||||
|
ContractChecks => ImmTy::from_bool(M::contract_checks(self)?, *self.tcx),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ const GATED_CFGS: &[GatedCfg] = &[
|
||||||
// (name in cfg, feature, function to check if the feature is enabled)
|
// (name in cfg, feature, function to check if the feature is enabled)
|
||||||
(sym::overflow_checks, sym::cfg_overflow_checks, Features::cfg_overflow_checks),
|
(sym::overflow_checks, sym::cfg_overflow_checks, Features::cfg_overflow_checks),
|
||||||
(sym::ub_checks, sym::cfg_ub_checks, Features::cfg_ub_checks),
|
(sym::ub_checks, sym::cfg_ub_checks, Features::cfg_ub_checks),
|
||||||
|
(sym::contract_checks, sym::cfg_contract_checks, Features::cfg_contract_checks),
|
||||||
(sym::target_thread_local, sym::cfg_target_thread_local, Features::cfg_target_thread_local),
|
(sym::target_thread_local, sym::cfg_target_thread_local, Features::cfg_target_thread_local),
|
||||||
(
|
(
|
||||||
sym::target_has_atomic_equal_alignment,
|
sym::target_has_atomic_equal_alignment,
|
||||||
|
|
|
@ -403,6 +403,8 @@ declare_features! (
|
||||||
(unstable, c_variadic, "1.34.0", Some(44930)),
|
(unstable, c_variadic, "1.34.0", Some(44930)),
|
||||||
/// Allows the use of `#[cfg(<true/false>)]`.
|
/// Allows the use of `#[cfg(<true/false>)]`.
|
||||||
(unstable, cfg_boolean_literals, "1.83.0", Some(131204)),
|
(unstable, cfg_boolean_literals, "1.83.0", Some(131204)),
|
||||||
|
/// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled.
|
||||||
|
(unstable, cfg_contract_checks, "CURRENT_RUSTC_VERSION", Some(133866)),
|
||||||
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
|
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
|
||||||
(unstable, cfg_overflow_checks, "1.71.0", Some(111466)),
|
(unstable, cfg_overflow_checks, "1.71.0", Some(111466)),
|
||||||
/// Provides the relocation model information as cfg entry
|
/// Provides the relocation model information as cfg entry
|
||||||
|
|
|
@ -132,6 +132,9 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
|
||||||
| sym::aggregate_raw_ptr
|
| sym::aggregate_raw_ptr
|
||||||
| sym::ptr_metadata
|
| sym::ptr_metadata
|
||||||
| sym::ub_checks
|
| sym::ub_checks
|
||||||
|
| sym::contract_checks
|
||||||
|
| sym::contract_check_requires
|
||||||
|
| sym::contract_check_ensures
|
||||||
| sym::fadd_algebraic
|
| sym::fadd_algebraic
|
||||||
| sym::fsub_algebraic
|
| sym::fsub_algebraic
|
||||||
| sym::fmul_algebraic
|
| sym::fmul_algebraic
|
||||||
|
@ -219,6 +222,18 @@ pub fn check_intrinsic_type(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
(n_tps, 0, 0, inputs, output, hir::Safety::Unsafe)
|
(n_tps, 0, 0, inputs, output, hir::Safety::Unsafe)
|
||||||
|
} else if intrinsic_name == sym::contract_check_ensures {
|
||||||
|
// contract_check_ensures::<'a, Ret, C>(&'a Ret, C) -> bool
|
||||||
|
// where C: impl Fn(&'a Ret) -> bool,
|
||||||
|
//
|
||||||
|
// so: two type params, one lifetime param, 0 const params, two inputs, returns boolean
|
||||||
|
|
||||||
|
let p = generics.param_at(0, tcx);
|
||||||
|
let r = ty::Region::new_early_param(tcx, p.to_early_bound_region_data());
|
||||||
|
let ref_ret = Ty::new_imm_ref(tcx, r, param(1));
|
||||||
|
// let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon };
|
||||||
|
// let ref_ret = Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0));
|
||||||
|
(2, 1, 0, vec![ref_ret, param(2)], tcx.types.bool, hir::Safety::Safe)
|
||||||
} else {
|
} else {
|
||||||
let safety = intrinsic_operation_unsafety(tcx, intrinsic_id);
|
let safety = intrinsic_operation_unsafety(tcx, intrinsic_id);
|
||||||
let (n_tps, n_cts, inputs, output) = match intrinsic_name {
|
let (n_tps, n_cts, inputs, output) = match intrinsic_name {
|
||||||
|
@ -610,6 +625,11 @@ pub fn check_intrinsic_type(
|
||||||
|
|
||||||
sym::box_new => (1, 0, vec![param(0)], Ty::new_box(tcx, param(0))),
|
sym::box_new => (1, 0, vec![param(0)], Ty::new_box(tcx, param(0))),
|
||||||
|
|
||||||
|
// contract_checks() -> bool
|
||||||
|
sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool),
|
||||||
|
// contract_check_requires::<C>(C) -> bool, where C: impl Fn() -> bool
|
||||||
|
sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.bool),
|
||||||
|
|
||||||
sym::simd_eq
|
sym::simd_eq
|
||||||
| sym::simd_ne
|
| sym::simd_ne
|
||||||
| sym::simd_lt
|
| sym::simd_lt
|
||||||
|
|
|
@ -1103,6 +1103,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
||||||
NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
|
NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
|
||||||
NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
|
NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
|
||||||
NullOp::UbChecks => write!(fmt, "UbChecks()"),
|
NullOp::UbChecks => write!(fmt, "UbChecks()"),
|
||||||
|
NullOp::ContractChecks => write!(fmt, "ContractChecks()"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ThreadLocalRef(did) => ty::tls::with(|tcx| {
|
ThreadLocalRef(did) => ty::tls::with(|tcx| {
|
||||||
|
|
|
@ -1591,6 +1591,9 @@ pub enum NullOp<'tcx> {
|
||||||
/// Returns whether we should perform some UB-checking at runtime.
|
/// Returns whether we should perform some UB-checking at runtime.
|
||||||
/// See the `ub_checks` intrinsic docs for details.
|
/// See the `ub_checks` intrinsic docs for details.
|
||||||
UbChecks,
|
UbChecks,
|
||||||
|
/// Returns whether we should perform contract-checking at runtime.
|
||||||
|
/// See the `contract_checks` intrinsic docs for details.
|
||||||
|
ContractChecks,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
|
|
@ -230,7 +230,8 @@ impl<'tcx> Rvalue<'tcx> {
|
||||||
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
|
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
|
||||||
tcx.types.usize
|
tcx.types.usize
|
||||||
}
|
}
|
||||||
Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool,
|
Rvalue::NullaryOp(NullOp::ContractChecks, _)
|
||||||
|
| Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool,
|
||||||
Rvalue::Aggregate(ref ak, ref ops) => match **ak {
|
Rvalue::Aggregate(ref ak, ref ops) => match **ak {
|
||||||
AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
|
AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
|
||||||
AggregateKind::Tuple => {
|
AggregateKind::Tuple => {
|
||||||
|
|
|
@ -417,7 +417,11 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
|
||||||
| Rvalue::Discriminant(..)
|
| Rvalue::Discriminant(..)
|
||||||
| Rvalue::Len(..)
|
| Rvalue::Len(..)
|
||||||
| Rvalue::NullaryOp(
|
| Rvalue::NullaryOp(
|
||||||
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbChecks,
|
NullOp::SizeOf
|
||||||
|
| NullOp::AlignOf
|
||||||
|
| NullOp::OffsetOf(..)
|
||||||
|
| NullOp::UbChecks
|
||||||
|
| NullOp::ContractChecks,
|
||||||
_,
|
_,
|
||||||
) => {}
|
) => {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -545,6 +545,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
||||||
.offset_of_subfield(self.typing_env(), layout, fields.iter())
|
.offset_of_subfield(self.typing_env(), layout, fields.iter())
|
||||||
.bytes(),
|
.bytes(),
|
||||||
NullOp::UbChecks => return None,
|
NullOp::UbChecks => return None,
|
||||||
|
NullOp::ContractChecks => return None,
|
||||||
};
|
};
|
||||||
let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
|
let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
|
||||||
let imm = ImmTy::from_uint(val, usize_layout);
|
let imm = ImmTy::from_uint(val, usize_layout);
|
||||||
|
|
|
@ -629,6 +629,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||||
.offset_of_subfield(self.typing_env, op_layout, fields.iter())
|
.offset_of_subfield(self.typing_env, op_layout, fields.iter())
|
||||||
.bytes(),
|
.bytes(),
|
||||||
NullOp::UbChecks => return None,
|
NullOp::UbChecks => return None,
|
||||||
|
NullOp::ContractChecks => return None,
|
||||||
};
|
};
|
||||||
ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into()
|
ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,17 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
|
||||||
});
|
});
|
||||||
terminator.kind = TerminatorKind::Goto { target };
|
terminator.kind = TerminatorKind::Goto { target };
|
||||||
}
|
}
|
||||||
|
sym::contract_checks => {
|
||||||
|
let target = target.unwrap();
|
||||||
|
block.statements.push(Statement {
|
||||||
|
source_info: terminator.source_info,
|
||||||
|
kind: StatementKind::Assign(Box::new((
|
||||||
|
*destination,
|
||||||
|
Rvalue::NullaryOp(NullOp::ContractChecks, tcx.types.bool),
|
||||||
|
))),
|
||||||
|
});
|
||||||
|
terminator.kind = TerminatorKind::Goto { target };
|
||||||
|
}
|
||||||
sym::forget => {
|
sym::forget => {
|
||||||
let target = target.unwrap();
|
let target = target.unwrap();
|
||||||
block.statements.push(Statement {
|
block.statements.push(Statement {
|
||||||
|
|
|
@ -457,6 +457,7 @@ impl<'tcx> Validator<'_, 'tcx> {
|
||||||
NullOp::AlignOf => {}
|
NullOp::AlignOf => {}
|
||||||
NullOp::OffsetOf(_) => {}
|
NullOp::OffsetOf(_) => {}
|
||||||
NullOp::UbChecks => {}
|
NullOp::UbChecks => {}
|
||||||
|
NullOp::ContractChecks => {}
|
||||||
},
|
},
|
||||||
|
|
||||||
Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable),
|
Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable),
|
||||||
|
|
|
@ -1379,7 +1379,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
Rvalue::Repeat(_, _)
|
Rvalue::Repeat(_, _)
|
||||||
| Rvalue::ThreadLocalRef(_)
|
| Rvalue::ThreadLocalRef(_)
|
||||||
| Rvalue::RawPtr(_, _)
|
| Rvalue::RawPtr(_, _)
|
||||||
| Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks, _)
|
| Rvalue::NullaryOp(
|
||||||
|
NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks | NullOp::ContractChecks,
|
||||||
|
_,
|
||||||
|
)
|
||||||
| Rvalue::Discriminant(_) => {}
|
| Rvalue::Discriminant(_) => {}
|
||||||
|
|
||||||
Rvalue::WrapUnsafeBinder(op, ty) => {
|
Rvalue::WrapUnsafeBinder(op, ty) => {
|
||||||
|
|
|
@ -119,6 +119,7 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) {
|
||||||
(sym::overflow_checks, None) => disallow(cfg, "-C overflow-checks"),
|
(sym::overflow_checks, None) => disallow(cfg, "-C overflow-checks"),
|
||||||
(sym::debug_assertions, None) => disallow(cfg, "-C debug-assertions"),
|
(sym::debug_assertions, None) => disallow(cfg, "-C debug-assertions"),
|
||||||
(sym::ub_checks, None) => disallow(cfg, "-Z ub-checks"),
|
(sym::ub_checks, None) => disallow(cfg, "-Z ub-checks"),
|
||||||
|
(sym::contract_checks, None) => disallow(cfg, "-Z contract-checks"),
|
||||||
(sym::sanitize, None | Some(_)) => disallow(cfg, "-Z sanitizer"),
|
(sym::sanitize, None | Some(_)) => disallow(cfg, "-Z sanitizer"),
|
||||||
(
|
(
|
||||||
sym::sanitizer_cfi_generalize_pointers | sym::sanitizer_cfi_normalize_integers,
|
sym::sanitizer_cfi_generalize_pointers | sym::sanitizer_cfi_normalize_integers,
|
||||||
|
@ -300,6 +301,11 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg {
|
||||||
if sess.is_nightly_build() && sess.opts.unstable_opts.emscripten_wasm_eh {
|
if sess.is_nightly_build() && sess.opts.unstable_opts.emscripten_wasm_eh {
|
||||||
ins_none!(sym::emscripten_wasm_eh);
|
ins_none!(sym::emscripten_wasm_eh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sess.contract_checks() {
|
||||||
|
ins_none!(sym::contract_checks);
|
||||||
|
}
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,6 +470,7 @@ impl CheckCfg {
|
||||||
ins!(sym::target_thread_local, no_values);
|
ins!(sym::target_thread_local, no_values);
|
||||||
|
|
||||||
ins!(sym::ub_checks, no_values);
|
ins!(sym::ub_checks, no_values);
|
||||||
|
ins!(sym::contract_checks, no_values);
|
||||||
|
|
||||||
ins!(sym::unix, no_values);
|
ins!(sym::unix, no_values);
|
||||||
ins!(sym::windows, no_values);
|
ins!(sym::windows, no_values);
|
||||||
|
|
|
@ -2114,6 +2114,8 @@ options! {
|
||||||
"the backend to use"),
|
"the backend to use"),
|
||||||
combine_cgu: bool = (false, parse_bool, [TRACKED],
|
combine_cgu: bool = (false, parse_bool, [TRACKED],
|
||||||
"combine CGUs into a single one"),
|
"combine CGUs into a single one"),
|
||||||
|
contract_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
|
||||||
|
"emit runtime checks for contract pre- and post-conditions (default: no)"),
|
||||||
coverage_options: CoverageOptions = (CoverageOptions::default(), parse_coverage_options, [TRACKED],
|
coverage_options: CoverageOptions = (CoverageOptions::default(), parse_coverage_options, [TRACKED],
|
||||||
"control details of coverage instrumentation"),
|
"control details of coverage instrumentation"),
|
||||||
crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED],
|
crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED],
|
||||||
|
|
|
@ -709,6 +709,10 @@ impl Session {
|
||||||
self.opts.unstable_opts.ub_checks.unwrap_or(self.opts.debug_assertions)
|
self.opts.unstable_opts.ub_checks.unwrap_or(self.opts.debug_assertions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn contract_checks(&self) -> bool {
|
||||||
|
self.opts.unstable_opts.contract_checks.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn relocation_model(&self) -> RelocModel {
|
pub fn relocation_model(&self) -> RelocModel {
|
||||||
self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model)
|
self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model)
|
||||||
}
|
}
|
||||||
|
|
|
@ -291,6 +291,7 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
|
||||||
indices.iter().map(|idx| idx.stable(tables)).collect(),
|
indices.iter().map(|idx| idx.stable(tables)).collect(),
|
||||||
),
|
),
|
||||||
UbChecks => stable_mir::mir::NullOp::UbChecks,
|
UbChecks => stable_mir::mir::NullOp::UbChecks,
|
||||||
|
ContractChecks => stable_mir::mir::NullOp::ContractChecks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -566,6 +566,7 @@ symbols! {
|
||||||
cfg_attr,
|
cfg_attr,
|
||||||
cfg_attr_multi,
|
cfg_attr_multi,
|
||||||
cfg_boolean_literals,
|
cfg_boolean_literals,
|
||||||
|
cfg_contract_checks,
|
||||||
cfg_doctest,
|
cfg_doctest,
|
||||||
cfg_emscripten_wasm_eh,
|
cfg_emscripten_wasm_eh,
|
||||||
cfg_eval,
|
cfg_eval,
|
||||||
|
@ -675,6 +676,9 @@ symbols! {
|
||||||
const_ty_placeholder: "<const_ty>",
|
const_ty_placeholder: "<const_ty>",
|
||||||
constant,
|
constant,
|
||||||
constructor,
|
constructor,
|
||||||
|
contract_check_ensures,
|
||||||
|
contract_check_requires,
|
||||||
|
contract_checks,
|
||||||
convert_identity,
|
convert_identity,
|
||||||
copy,
|
copy,
|
||||||
copy_closures,
|
copy_closures,
|
||||||
|
|
|
@ -608,7 +608,8 @@ impl Rvalue {
|
||||||
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
|
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
|
||||||
Ok(Ty::usize_ty())
|
Ok(Ty::usize_ty())
|
||||||
}
|
}
|
||||||
Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()),
|
Rvalue::NullaryOp(NullOp::ContractChecks, _)
|
||||||
|
| Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()),
|
||||||
Rvalue::Aggregate(ak, ops) => match *ak {
|
Rvalue::Aggregate(ak, ops) => match *ak {
|
||||||
AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64),
|
AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64),
|
||||||
AggregateKind::Tuple => Ok(Ty::new_tuple(
|
AggregateKind::Tuple => Ok(Ty::new_tuple(
|
||||||
|
@ -1007,6 +1008,8 @@ pub enum NullOp {
|
||||||
OffsetOf(Vec<(VariantIdx, FieldIdx)>),
|
OffsetOf(Vec<(VariantIdx, FieldIdx)>),
|
||||||
/// cfg!(ub_checks), but at codegen time
|
/// cfg!(ub_checks), but at codegen time
|
||||||
UbChecks,
|
UbChecks,
|
||||||
|
/// cfg!(contract_checks), but at codegen time
|
||||||
|
ContractChecks,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Operand {
|
impl Operand {
|
||||||
|
|
|
@ -4044,6 +4044,38 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize)
|
||||||
// Runtime NOP
|
// Runtime NOP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether we should perform contract-checking at runtime.
|
||||||
|
///
|
||||||
|
/// This is meant to be similar to the ub_checks intrinsic, in terms
|
||||||
|
/// of not prematurely commiting at compile-time to whether contract
|
||||||
|
/// checking is turned on, so that we can specify contracts in libstd
|
||||||
|
/// and let an end user opt into turning them on.
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
#[rustc_const_unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
|
||||||
|
#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
|
||||||
|
#[inline(always)]
|
||||||
|
#[rustc_intrinsic]
|
||||||
|
pub const fn contract_checks() -> bool {
|
||||||
|
// FIXME: should this be `false` or `cfg!(contract_checks)`?
|
||||||
|
|
||||||
|
// cfg!(contract_checks)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
|
||||||
|
#[rustc_intrinsic]
|
||||||
|
pub fn contract_check_requires<C: FnOnce() -> bool>(c: C) -> bool {
|
||||||
|
c()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
|
||||||
|
#[rustc_intrinsic]
|
||||||
|
pub fn contract_check_ensures<'a, Ret, C: FnOnce(&'a Ret) -> bool>(ret: &'a Ret, c: C) -> bool {
|
||||||
|
c(ret)
|
||||||
|
}
|
||||||
|
|
||||||
/// The intrinsic will return the size stored in that vtable.
|
/// The intrinsic will return the size stored in that vtable.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
|
|
@ -179,7 +179,7 @@ fn check_rvalue<'tcx>(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks, _)
|
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, _)
|
||||||
| Rvalue::ShallowInitBox(_, _) => Ok(()),
|
| Rvalue::ShallowInitBox(_, _) => Ok(()),
|
||||||
Rvalue::UnaryOp(_, operand) => {
|
Rvalue::UnaryOp(_, operand) => {
|
||||||
let ty = operand.ty(body, tcx);
|
let ty = operand.ty(body, tcx);
|
||||||
|
|
|
@ -1150,6 +1150,11 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
||||||
interp_ok(ecx.tcx.sess.ub_checks())
|
interp_ok(ecx.tcx.sess.ub_checks())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn contract_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
|
||||||
|
interp_ok(ecx.tcx.sess.contract_checks())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn thread_local_static_pointer(
|
fn thread_local_static_pointer(
|
||||||
ecx: &mut MiriInterpCx<'tcx>,
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
|
|
23
tests/ui/contracts/contract-intrinsics.rs
Normal file
23
tests/ui/contracts/contract-intrinsics.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
//@ run-pass
|
||||||
|
//@ revisions: yes no none
|
||||||
|
//@ [yes] compile-flags: -Zcontract-checks=yes
|
||||||
|
//@ [no] compile-flags: -Zcontract-checks=no
|
||||||
|
#![feature(cfg_contract_checks, rustc_contracts, core_intrinsics)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(none)] // default: disabled
|
||||||
|
assert_eq!(core::intrinsics::contract_checks(), false);
|
||||||
|
|
||||||
|
#[cfg(yes)] // explicitly enabled
|
||||||
|
assert_eq!(core::intrinsics::contract_checks(), true);
|
||||||
|
|
||||||
|
#[cfg(no)] // explicitly disabled
|
||||||
|
assert_eq!(core::intrinsics::contract_checks(), false);
|
||||||
|
|
||||||
|
assert_eq!(core::intrinsics::contract_check_requires(|| true), true);
|
||||||
|
assert_eq!(core::intrinsics::contract_check_requires(|| false), false);
|
||||||
|
|
||||||
|
let doubles_to_two = { let old = 2; move |ret| ret + ret == old };
|
||||||
|
assert_eq!(core::intrinsics::contract_check_ensures(&1, doubles_to_two), true);
|
||||||
|
assert_eq!(core::intrinsics::contract_check_ensures(&2, doubles_to_two), false);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
|
||||||
|
pub fn contract_checks_are_enabled() -> bool {
|
||||||
|
cfg!(contract_checks) //~ ERROR `cfg(contract_checks)` is experimental
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
error[E0658]: `cfg(contract_checks)` is experimental and subject to change
|
||||||
|
--> $DIR/feature-gate-cfg-contract-checks.rs:4:10
|
||||||
|
|
|
||||||
|
LL | cfg!(contract_checks)
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
|
||||||
|
= help: add `#![feature(cfg_contract_checks)]` to the crate attributes to enable
|
||||||
|
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0658`.
|
Loading…
Add table
Add a link
Reference in a new issue