From 832fcfb64fdf47714b181bc29f2d93cb5a3383f4 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 31 Jan 2025 16:29:09 +1100 Subject: [PATCH 01/28] Introduce `DIBuilderBox`, an owning pointer to `DIBuilder` --- .../src/debuginfo/metadata.rs | 4 +- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 16 ++---- .../rustc_codegen_llvm/src/debuginfo/utils.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 51 +++++++++++++++++-- 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 3a0c7f007bdd2..56ae9607adffb 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -931,7 +931,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( unsafe { let compile_unit_file = llvm::LLVMRustDIBuilderCreateFile( - debug_context.builder, + debug_context.builder.as_ref(), name_in_debuginfo.as_c_char_ptr(), name_in_debuginfo.len(), work_dir.as_c_char_ptr(), @@ -944,7 +944,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( ); let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit( - debug_context.builder, + debug_context.builder.as_ref(), dwarf_const::DW_LANG_Rust, compile_unit_file, producer.as_c_char_ptr(), diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index b1ce52667bd6d..57549eb9f8d43 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -34,7 +34,7 @@ use crate::builder::Builder; use crate::common::{AsCCharPtr, CodegenCx}; use crate::llvm; use crate::llvm::debuginfo::{ - DIArray, DIBuilder, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DIType, + DIArray, DIBuilderBox, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DIType, DIVariable, }; use crate::value::Value; @@ -61,7 +61,7 @@ const DW_TAG_arg_variable: c_uint = 0x101; /// A context object for maintaining all state needed by the debuginfo module. pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { llmod: &'ll llvm::Module, - builder: &'ll mut DIBuilder<'ll>, + builder: DIBuilderBox<'ll>, created_files: RefCell, &'ll DIFile>>, type_map: metadata::TypeMap<'ll, 'tcx>, @@ -69,18 +69,10 @@ pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { recursion_marker_type: OnceCell<&'ll DIType>, } -impl Drop for CodegenUnitDebugContext<'_, '_> { - fn drop(&mut self) { - unsafe { - llvm::LLVMRustDIBuilderDispose(&mut *(self.builder as *mut _)); - } - } -} - impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { pub(crate) fn new(llmod: &'ll llvm::Module) -> Self { debug!("CodegenUnitDebugContext::new"); - let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) }; + let builder = DIBuilderBox::new(llmod); // DIBuilder inherits context from the module, so we'd better use the same one CodegenUnitDebugContext { llmod, @@ -93,7 +85,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { } pub(crate) fn finalize(&self, sess: &Session) { - unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder) }; + unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder.as_ref()) }; match sess.target.debuginfo_kind { DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym => { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index 6e84129347787..cc1d504b43017 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -41,7 +41,7 @@ pub(crate) fn debug_context<'a, 'll, 'tcx>( #[inline] #[allow(non_snake_case)] pub(crate) fn DIB<'a, 'll>(cx: &'a CodegenCx<'ll, '_>) -> &'a DIBuilder<'ll> { - cx.dbg_cx.as_ref().unwrap().builder + cx.dbg_cx.as_ref().unwrap().builder.as_ref() } pub(crate) fn get_namespace_for_item<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DIScope { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index cc7c5231aca58..c3752f36f5066 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -789,12 +789,50 @@ pub type DiagnosticHandlerTy = unsafe extern "C" fn(&DiagnosticInfo, *mut c_void pub type InlineAsmDiagHandlerTy = unsafe extern "C" fn(&SMDiagnostic, *const c_void, c_uint); pub mod debuginfo { + use std::ptr; + use bitflags::bitflags; use super::{InvariantOpaque, Metadata}; + use crate::llvm::{self, Module}; + /// Opaque target type for references to an LLVM debuginfo builder. + /// + /// `&'_ DIBuilder<'ll>` corresponds to `LLVMDIBuilderRef`, which is the + /// LLVM-C wrapper for `DIBuilder *`. + /// + /// Debuginfo builders are created and destroyed during codegen, so the + /// builder reference typically has a shorter lifetime than the LLVM + /// session (`'ll`) that it participates in. #[repr(C)] - pub struct DIBuilder<'a>(InvariantOpaque<'a>); + pub struct DIBuilder<'ll>(InvariantOpaque<'ll>); + + /// Owning pointer to a `DIBuilder<'ll>` that will dispose of the builder + /// when dropped. Use `.as_ref()` to get the underlying `&DIBuilder` + /// needed for debuginfo FFI calls. + pub(crate) struct DIBuilderBox<'ll> { + raw: ptr::NonNull>, + } + + impl<'ll> DIBuilderBox<'ll> { + pub(crate) fn new(llmod: &'ll Module) -> Self { + let raw = unsafe { llvm::LLVMCreateDIBuilder(llmod) }; + let raw = ptr::NonNull::new(raw).unwrap(); + Self { raw } + } + + pub(crate) fn as_ref(&self) -> &DIBuilder<'ll> { + // SAFETY: This is an owning pointer, so `&DIBuilder` is valid + // for as long as `&self` is. + unsafe { self.raw.as_ref() } + } + } + + impl<'ll> Drop for DIBuilderBox<'ll> { + fn drop(&mut self) { + unsafe { llvm::LLVMDisposeDIBuilder(self.raw) }; + } + } pub type DIDescriptor = Metadata; pub type DILocation = Metadata; @@ -1672,6 +1710,13 @@ unsafe extern "C" { ) -> &'a Value; } +// FFI bindings for `DIBuilder` functions in the LLVM-C API. +// Try to keep these in the same order as in `llvm/include/llvm-c/DebugInfo.h`. +unsafe extern "C" { + pub(crate) fn LLVMCreateDIBuilder<'ll>(M: &'ll Module) -> *mut DIBuilder<'ll>; + pub(crate) fn LLVMDisposeDIBuilder<'ll>(Builder: ptr::NonNull>); +} + #[link(name = "llvm-wrapper", kind = "static")] unsafe extern "C" { pub fn LLVMRustInstallErrorHandlers(); @@ -1939,10 +1984,6 @@ unsafe extern "C" { ValueLen: size_t, ); - pub fn LLVMRustDIBuilderCreate(M: &Module) -> &mut DIBuilder<'_>; - - pub fn LLVMRustDIBuilderDispose<'a>(Builder: &'a mut DIBuilder<'a>); - pub fn LLVMRustDIBuilderFinalize(Builder: &DIBuilder<'_>); pub fn LLVMRustDIBuilderCreateCompileUnit<'a>( From cd2af2dd9a82b564fd74f7d2b7876c120812f6ba Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 1 Feb 2025 13:38:12 +1100 Subject: [PATCH 02/28] Use `LLVMDIBuilderFinalize` --- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 ++-- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 4 ---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 57549eb9f8d43..cb84bede89336 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -85,7 +85,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { } pub(crate) fn finalize(&self, sess: &Session) { - unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder.as_ref()) }; + unsafe { llvm::LLVMDIBuilderFinalize(self.builder.as_ref()) }; match sess.target.debuginfo_kind { DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym => { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index c3752f36f5066..e6ffead845dd6 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1715,6 +1715,8 @@ unsafe extern "C" { unsafe extern "C" { pub(crate) fn LLVMCreateDIBuilder<'ll>(M: &'ll Module) -> *mut DIBuilder<'ll>; pub(crate) fn LLVMDisposeDIBuilder<'ll>(Builder: ptr::NonNull>); + + pub(crate) fn LLVMDIBuilderFinalize<'ll>(Builder: &DIBuilder<'ll>); } #[link(name = "llvm-wrapper", kind = "static")] @@ -1984,8 +1986,6 @@ unsafe extern "C" { ValueLen: size_t, ); - pub fn LLVMRustDIBuilderFinalize(Builder: &DIBuilder<'_>); - pub fn LLVMRustDIBuilderCreateCompileUnit<'a>( Builder: &DIBuilder<'a>, Lang: c_uint, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 7ff316ba83a61..6c5bfc1ac7814 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1003,10 +1003,6 @@ extern "C" void LLVMRustDIBuilderDispose(LLVMDIBuilderRef Builder) { delete unwrap(Builder); } -extern "C" void LLVMRustDIBuilderFinalize(LLVMDIBuilderRef Builder) { - unwrap(Builder)->finalize(); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit( LLVMDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef, const char *Producer, size_t ProducerLen, bool isOptimized, From 878ab125a107d8f8036cc41a2db6abcb68996226 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 1 Feb 2025 13:43:30 +1100 Subject: [PATCH 03/28] Use `LLVMDIBuilderCreateNameSpace` --- .../src/debuginfo/namespace.rs | 8 +++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 27 ++++++++++--------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 8 ------ 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs index 33d9bc238903e..b4d639368b005 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs @@ -5,7 +5,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::ty::{self, Instance}; use super::utils::{DIB, debug_context}; -use crate::common::{AsCCharPtr, CodegenCx}; +use crate::common::CodegenCx; use crate::llvm; use crate::llvm::debuginfo::DIScope; @@ -33,12 +33,12 @@ pub(crate) fn item_namespace<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'l }; let scope = unsafe { - llvm::LLVMRustDIBuilderCreateNameSpace( + llvm::LLVMDIBuilderCreateNameSpace( DIB(cx), parent_scope, - namespace_name_string.as_c_char_ptr(), + namespace_name_string.as_ptr(), namespace_name_string.len(), - false, // ExportSymbols (only relevant for C++ anonymous namespaces) + llvm::False, // ExportSymbols (only relevant for C++ anonymous namespaces) ) }; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e6ffead845dd6..d7324779ea8cc 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -5,17 +5,19 @@ use std::fmt::Debug; use std::marker::PhantomData; use std::ptr; -use libc::{c_char, c_int, c_uint, c_ulonglong, c_void, size_t}; +use bitflags::bitflags; +use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_void, size_t}; use rustc_macros::TryFromU32; use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator, - DIFile, DIFlags, DIGlobalVariableExpression, DILexicalBlock, DILocation, DINameSpace, - DISPFlags, DIScope, DISubprogram, DISubrange, DITemplateTypeParameter, DIType, DIVariable, - DebugEmissionKind, DebugNameTableKind, + DIFile, DIFlags, DIGlobalVariableExpression, DILexicalBlock, DILocation, DISPFlags, DIScope, + DISubprogram, DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, + DebugNameTableKind, }; +use crate::llvm; /// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`, /// which has a different ABI from Rust or C++ `bool`. @@ -952,7 +954,6 @@ pub mod debuginfo { } } -use bitflags::bitflags; // These values **must** match with LLVMRustAllocKindFlags bitflags! { #[repr(transparent)] @@ -1717,6 +1718,14 @@ unsafe extern "C" { pub(crate) fn LLVMDisposeDIBuilder<'ll>(Builder: ptr::NonNull>); pub(crate) fn LLVMDIBuilderFinalize<'ll>(Builder: &DIBuilder<'ll>); + + pub(crate) fn LLVMDIBuilderCreateNameSpace<'ll>( + Builder: &DIBuilder<'ll>, + ParentScope: Option<&'ll Metadata>, + Name: *const c_uchar, + NameLen: size_t, + ExportSymbols: llvm::Bool, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2286,14 +2295,6 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DITemplateTypeParameter; - pub fn LLVMRustDIBuilderCreateNameSpace<'a>( - Builder: &DIBuilder<'a>, - Scope: Option<&'a DIScope>, - Name: *const c_char, - NameLen: size_t, - ExportSymbols: bool, - ) -> &'a DINameSpace; - pub fn LLVMRustDICompositeTypeReplaceArrays<'a>( Builder: &DIBuilder<'a>, CompositeType: &'a DIType, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 6c5bfc1ac7814..2b561dc353474 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1321,14 +1321,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter( unwrapDI(Ty), IsDefault)); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateNameSpace(LLVMDIBuilderRef Builder, - LLVMMetadataRef Scope, const char *Name, - size_t NameLen, bool ExportSymbols) { - return wrap(unwrap(Builder)->createNameSpace( - unwrapDI(Scope), StringRef(Name, NameLen), ExportSymbols)); -} - extern "C" void LLVMRustDICompositeTypeReplaceArrays( LLVMDIBuilderRef Builder, LLVMMetadataRef CompositeTy, LLVMMetadataRef Elements, LLVMMetadataRef Params) { From 70d41bc7110ed05c8befb65403b028e1fbaf2c5a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 1 Feb 2025 13:50:01 +1100 Subject: [PATCH 04/28] Use `LLVMDIBuilderCreateLexicalBlock` --- .../src/debuginfo/create_scope_map.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 16 ++++++++-------- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 8 -------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs index 11eb9651af6c5..f52991b369797 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs @@ -127,7 +127,7 @@ fn make_mir_scope<'ll, 'tcx>( }) } None => unsafe { - llvm::LLVMRustDIBuilderCreateLexicalBlock( + llvm::LLVMDIBuilderCreateLexicalBlock( DIB(cx), parent_scope.dbg_scope, file_metadata, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index d7324779ea8cc..ed2ea9701863b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1726,6 +1726,14 @@ unsafe extern "C" { NameLen: size_t, ExportSymbols: llvm::Bool, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateLexicalBlock<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + File: &'ll Metadata, + Line: c_uint, + Column: c_uint, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2157,14 +2165,6 @@ unsafe extern "C" { Type: &'a DIType, ) -> &'a DIDerivedType; - pub fn LLVMRustDIBuilderCreateLexicalBlock<'a>( - Builder: &DIBuilder<'a>, - Scope: &'a DIScope, - File: &'a DIFile, - Line: c_uint, - Col: c_uint, - ) -> &'a DILexicalBlock; - pub fn LLVMRustDIBuilderCreateLexicalBlockFile<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 2b561dc353474..0c3fc6c467106 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1179,14 +1179,6 @@ LLVMRustDIBuilderCreateQualifiedType(LLVMDIBuilderRef Builder, unsigned Tag, unwrap(Builder)->createQualifiedType(Tag, unwrapDI(Type))); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateLexicalBlock(LLVMDIBuilderRef Builder, - LLVMMetadataRef Scope, LLVMMetadataRef File, - unsigned Line, unsigned Col) { - return wrap(unwrap(Builder)->createLexicalBlock( - unwrapDI(Scope), unwrapDI(File), Line, Col)); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlockFile( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, LLVMMetadataRef File) { return wrap(unwrap(Builder)->createLexicalBlockFile( From 949b4673ceebd767cc0b138392b420a050dc6ce0 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 1 Feb 2025 13:55:44 +1100 Subject: [PATCH 05/28] Use `LLVMDIBuilderCreateLexicalBlockFile` --- .../src/debuginfo/metadata.rs | 9 ++++++++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 18 +++++++++--------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 6 ------ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 56ae9607adffb..f497ba95661df 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1641,7 +1641,14 @@ pub(crate) fn extend_scope_to_file<'ll>( file: &SourceFile, ) -> &'ll DILexicalBlock { let file_metadata = file_metadata(cx, file); - unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlockFile(DIB(cx), scope_metadata, file_metadata) } + unsafe { + llvm::LLVMDIBuilderCreateLexicalBlockFile( + DIB(cx), + scope_metadata, + file_metadata, + /* Discriminator (default) */ 0u32, + ) + } } fn tuple_field_name(field_index: usize) -> Cow<'static, str> { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index ed2ea9701863b..f7713e9a726eb 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -13,9 +13,8 @@ use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator, - DIFile, DIFlags, DIGlobalVariableExpression, DILexicalBlock, DILocation, DISPFlags, DIScope, - DISubprogram, DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, - DebugNameTableKind, + DIFile, DIFlags, DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, + DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, }; use crate::llvm; @@ -1734,6 +1733,13 @@ unsafe extern "C" { Line: c_uint, Column: c_uint, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateLexicalBlockFile<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + File: &'ll Metadata, + Discriminator: c_uint, // (optional "DWARF path discriminator"; default is 0) + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2165,12 +2171,6 @@ unsafe extern "C" { Type: &'a DIType, ) -> &'a DIDerivedType; - pub fn LLVMRustDIBuilderCreateLexicalBlockFile<'a>( - Builder: &DIBuilder<'a>, - Scope: &'a DIScope, - File: &'a DIFile, - ) -> &'a DILexicalBlock; - pub fn LLVMRustDIBuilderCreateStaticVariable<'a>( Builder: &DIBuilder<'a>, Context: Option<&'a DIScope>, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 0c3fc6c467106..6805ecfed0328 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1179,12 +1179,6 @@ LLVMRustDIBuilderCreateQualifiedType(LLVMDIBuilderRef Builder, unsigned Tag, unwrap(Builder)->createQualifiedType(Tag, unwrapDI(Type))); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlockFile( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, LLVMMetadataRef File) { - return wrap(unwrap(Builder)->createLexicalBlockFile( - unwrapDI(Scope), unwrapDI(File))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( LLVMDIBuilderRef Builder, LLVMMetadataRef Context, const char *Name, size_t NameLen, const char *LinkageName, size_t LinkageNameLen, From 8ddd9c38f6165d40ca1ab82e1d2bf9890a047c3a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 1 Feb 2025 14:00:20 +1100 Subject: [PATCH 06/28] Use `LLVMDIBuilderCreateDebugLocation` The LLVM-C binding takes an explicit context, whereas our binding obtained the context from the scope argument. --- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 14 ++++++++------ compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 10 ---------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index cb84bede89336..496178c6b1d94 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -574,7 +574,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { (line, col) }; - unsafe { llvm::LLVMRustDIBuilderCreateDebugLocation(line, col, scope, inlined_at) } + unsafe { llvm::LLVMDIBuilderCreateDebugLocation(self.llcx, line, col, scope, inlined_at) } } fn create_vtable_debuginfo( diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index f7713e9a726eb..95896916adc7d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1740,6 +1740,14 @@ unsafe extern "C" { File: &'ll Metadata, Discriminator: c_uint, // (optional "DWARF path discriminator"; default is 0) ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateDebugLocation<'ll>( + Ctx: &'ll Context, + Line: c_uint, + Column: c_uint, + Scope: &'ll Metadata, + InlinedAt: Option<&'ll Metadata>, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2302,12 +2310,6 @@ unsafe extern "C" { Params: Option<&'a DIArray>, ); - pub fn LLVMRustDIBuilderCreateDebugLocation<'a>( - Line: c_uint, - Column: c_uint, - Scope: &'a DIScope, - InlinedAt: Option<&'a DILocation>, - ) -> &'a DILocation; pub fn LLVMRustDILocationCloneWithBaseDiscriminator<'a>( Location: &'a DILocation, BD: c_uint, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 6805ecfed0328..76ad7fc890911 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1315,16 +1315,6 @@ extern "C" void LLVMRustDICompositeTypeReplaceArrays( DINodeArray(unwrap(Params))); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateDebugLocation(unsigned Line, unsigned Column, - LLVMMetadataRef ScopeRef, - LLVMMetadataRef InlinedAt) { - MDNode *Scope = unwrapDIPtr(ScopeRef); - DILocation *Loc = DILocation::get(Scope->getContext(), Line, Column, Scope, - unwrapDIPtr(InlinedAt)); - return wrap(Loc); -} - extern "C" LLVMMetadataRef LLVMRustDILocationCloneWithBaseDiscriminator(LLVMMetadataRef Location, unsigned BD) { From 5413d2bd6fd23103a8f44b306cdc6da1e109b6a2 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 8 Dec 2024 00:10:39 +1100 Subject: [PATCH 07/28] Add FIXME for auditing optional parameters passed to DIBuilder --- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 95896916adc7d..222e69df33586 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1712,6 +1712,10 @@ unsafe extern "C" { // FFI bindings for `DIBuilder` functions in the LLVM-C API. // Try to keep these in the same order as in `llvm/include/llvm-c/DebugInfo.h`. +// +// FIXME(#134001): Audit all `Option` parameters, especially in lists, to check +// that they really are nullable on the C/C++ side. LLVM doesn't appear to +// actually document which ones are nullable. unsafe extern "C" { pub(crate) fn LLVMCreateDIBuilder<'ll>(M: &'ll Module) -> *mut DIBuilder<'ll>; pub(crate) fn LLVMDisposeDIBuilder<'ll>(Builder: ptr::NonNull>); From c3f2930edc22407455b303612f46a684f23622b1 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 12 Dec 2024 20:41:01 +1100 Subject: [PATCH 08/28] Explain why (some) pointer/length strings are `*const c_uchar` --- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 222e69df33586..5c53a419a6ccc 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1,3 +1,15 @@ +//! Bindings to the LLVM-C API (`LLVM*`), and to our own `extern "C"` wrapper +//! functions around the unstable LLVM C++ API (`LLVMRust*`). +//! +//! ## Passing pointer/length strings as `*const c_uchar` +//! +//! Normally it's a good idea for Rust-side bindings to match the corresponding +//! C-side function declarations as closely as possible. But when passing `&str` +//! or `&[u8]` data as a pointer/length pair, it's more convenient to declare +//! the Rust-side pointer as `*const c_uchar` instead of `*const c_char`. +//! Both pointer types have the same ABI, and using `*const c_uchar` avoids +//! the need for an extra cast from `*const u8` on the Rust side. + #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)] From f5d5210d26d29faad12c6aa7ef55aefa8d9f6ce9 Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Sat, 1 Feb 2025 21:55:50 +0000 Subject: [PATCH 09/28] rustdoc-book: Clean up section on `--output-format` --- src/doc/rustdoc/src/unstable-features.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 7eb1b8df2333d..3abd538d372c4 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -330,7 +330,7 @@ the source. ## `--show-type-layout`: add a section to each type's docs describing its memory layout -* Tracking issue: [#113248](https://github.com/rust-lang/rust/issues/113248) + * Tracking issue: [#113248](https://github.com/rust-lang/rust/issues/113248) Using this flag looks like this: @@ -526,9 +526,10 @@ use `-o -`. ### json + * Tracking Issue: [#76578](https://github.com/rust-lang/rust/issues/76578) + `--output-format json` emits documentation in the experimental -[JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types/). `--output-format html` has no effect, -and is also accepted on stable toolchains. +[JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types/). JSON Output for toolchain crates (`std`, `alloc`, `core`, `test`, and `proc_macro`) is available via the `rust-docs-json` rustup component. @@ -546,11 +547,11 @@ information. ### doctest + * Tracking issue: [#134529](https://github.com/rust-lang/rust/issues/134529) + `--output-format doctest` emits JSON on stdout which gives you information about doctests in the provided crate. -Tracking issue: [#134529](https://github.com/rust-lang/rust/issues/134529) - You can use this option like this: ```bash @@ -606,6 +607,11 @@ The generated output (formatted) will look like this: * `doctest_code` is the code modified by rustdoc that will be run. If there is a fatal syntax error, this field will not be present. * `name` is the name generated by rustdoc which represents this doctest. +### html + +`--output-format html` has no effect, as the default output is HTML. This is +accepted on stable, even though the other options for this flag aren't. + ## `--enable-per-target-ignores`: allow `ignore-foo` style filters for doctests * Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245) From b909c36f40035bcc0a25f8734ee6480685cba1b1 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 3 Feb 2025 07:34:47 -0500 Subject: [PATCH 10/28] Remove allocations in `FnCtxt` checks. --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 172 ++++++++---------- 1 file changed, 77 insertions(+), 95 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index bbe653714962e..7ee246e07743c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -13,7 +13,7 @@ use rustc_hir::{ExprKind, HirId, Node, QPath}; use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; -use rustc_index::IndexVec; +use rustc_index::{Idx, IndexVec}; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TypeTrace}; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::TypeError; @@ -2374,11 +2374,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let check_for_matched_generics = || { if matched_inputs.iter().any(|x| x.is_some()) - && params_with_generics.iter().any(|x| x.1.is_some()) + && params_with_generics.iter().any(|(x, _)| x.is_some()) { - for &(idx, generic, _) in ¶ms_with_generics { + for (idx, (generic, _)) in params_with_generics.iter_enumerated() { // Param has to have a generic and be matched to be relevant - if matched_inputs[idx.into()].is_none() { + if matched_inputs[idx].is_none() { continue; } @@ -2386,10 +2386,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; }; - for unmatching_idx in idx + 1..params_with_generics.len() { - if matched_inputs[unmatching_idx.into()].is_none() + for unmatching_idx in + idx.plus(1)..ExpectedIdx::from_usize(params_with_generics.len()) + { + if matched_inputs[unmatching_idx].is_none() && let Some(unmatched_idx_param_generic) = - params_with_generics[unmatching_idx].1 + params_with_generics[unmatching_idx].0 && unmatched_idx_param_generic.name.ident() == generic.name.ident() { @@ -2404,10 +2406,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let check_for_matched_generics = check_for_matched_generics(); - for &(idx, generic_param, param) in - params_with_generics.iter().filter(|&(idx, _, _)| { + for (idx, &(generic_param, param)) in + params_with_generics.iter_enumerated().filter(|&(idx, _)| { check_for_matched_generics - || expected_idx.is_none_or(|expected_idx| expected_idx == *idx) + || expected_idx + .is_none_or(|expected_idx| expected_idx == idx.as_usize()) }) { let Some(generic_param) = generic_param else { @@ -2415,29 +2418,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; }; - let other_params_matched: Vec<(usize, &hir::Param<'_>)> = params_with_generics - .iter() - .filter(|(other_idx, other_generic_param, _)| { - if *other_idx == idx { - return false; - } - let Some(other_generic_param) = other_generic_param else { - return false; - }; - if matched_inputs[idx.into()].is_none() - && matched_inputs[(*other_idx).into()].is_none() - { - return false; - } - if matched_inputs[idx.into()].is_some() - && matched_inputs[(*other_idx).into()].is_some() - { - return false; - } - other_generic_param.name.ident() == generic_param.name.ident() - }) - .map(|&(other_idx, _, other_param)| (other_idx, other_param)) - .collect(); + let other_params_matched: Vec<(ExpectedIdx, &hir::Param<'_>)> = + params_with_generics + .iter_enumerated() + .filter(|&(other_idx, &(other_generic_param, _))| { + if other_idx == idx { + return false; + } + let Some(other_generic_param) = other_generic_param else { + return false; + }; + if matched_inputs[idx].is_none() + && matched_inputs[other_idx].is_none() + { + return false; + } + if matched_inputs[idx].is_some() + && matched_inputs[other_idx].is_some() + { + return false; + } + other_generic_param.name.ident() == generic_param.name.ident() + }) + .map(|(other_idx, &(_, other_param))| (other_idx, other_param)) + .collect(); if !other_params_matched.is_empty() { let other_param_matched_names: Vec = other_params_matched @@ -2447,16 +2451,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { format!("`{ident}`") } else { - format!("parameter #{}", idx + 1) + format!("parameter #{}", idx.as_u32() + 1) } }) .collect(); let matched_ty = self - .resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1) + .resolve_vars_if_possible(formal_and_expected_inputs[idx].1) .sort_string(self.tcx); - if matched_inputs[idx.into()].is_some() { + if matched_inputs[idx].is_some() { spans.push_span_label( param.span, format!( @@ -2502,19 +2506,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) { let param_idents_matching: Vec = params_with_generics - .iter() - .filter(|(_, generic, _)| { + .iter_enumerated() + .filter(|&(_, &(generic, _))| { if let Some(generic) = generic { generic.name.ident() == generic_param.name.ident() } else { false } }) - .map(|(idx, _, param)| { + .map(|(idx, &(_, param))| { if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind { format!("`{ident}`") } else { - format!("parameter #{}", idx + 1) + format!("parameter #{}", idx.as_u32() + 1) } }) .collect(); @@ -2607,12 +2611,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(params_with_generics) = self.get_hir_params_with_generics(def_id, is_method) { debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); - for &(idx, generic_param, _) in ¶ms_with_generics { - if matched_inputs[idx.into()].is_none() { + for (idx, (generic_param, _)) in params_with_generics.iter_enumerated() { + if matched_inputs[idx].is_none() { continue; } - let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.into()) else { + let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.to_provided_idx()) + else { continue; }; @@ -2620,32 +2625,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; }; - let mut idxs_matched: Vec = vec![]; - for &(other_idx, _, _) in - params_with_generics.iter().filter(|&&(other_idx, other_generic_param, _)| { + let idxs_matched = params_with_generics + .iter_enumerated() + .filter(|&(other_idx, (other_generic_param, _))| { if other_idx == idx { return false; } let Some(other_generic_param) = other_generic_param else { return false; }; - if matched_inputs[other_idx.into()].is_some() { + if matched_inputs[other_idx].is_some() { return false; } other_generic_param.name.ident() == generic_param.name.ident() }) - { - idxs_matched.push(other_idx); - } + .count(); - if idxs_matched.is_empty() { + if idxs_matched == 0 { continue; } let expected_display_type = self .resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1) .sort_string(self.tcx); - let label = if idxs_matched.len() == params_with_generics.len() - 1 { + let label = if idxs_matched == params_with_generics.len() - 1 { format!( "expected all arguments to be this {} type because they need to match the type of this parameter", expected_display_type @@ -2664,60 +2667,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Returns the parameters of a function, with their generic parameters if those are the full - /// type of that parameter. Returns `None` if the function body is unavailable (eg is an instrinsic). + /// type of that parameter. Returns `None` if the function has no generics or the body is + /// unavailable (eg is an instrinsic). fn get_hir_params_with_generics( &self, def_id: DefId, is_method: bool, - ) -> Option>, &hir::Param<'_>)>> { + ) -> Option>, &hir::Param<'_>)>> { let fn_node = self.tcx.hir().get_if_local(def_id)?; let fn_decl = fn_node.fn_decl()?; + let generic_params = fn_node.generics()?.params; - let generic_params: Vec>> = fn_decl - .inputs - .into_iter() - .skip(if is_method { 1 } else { 0 }) - .map(|param| { - if let hir::TyKind::Path(QPath::Resolved( - _, - hir::Path { res: Res::Def(_, res_def_id), .. }, - )) = param.kind - { - fn_node - .generics() - .into_iter() - .flat_map(|generics| generics.params) - .find(|param| ¶m.def_id.to_def_id() == res_def_id) - } else { - None - } - }) - .collect(); + // Remove both the receiver and variadic arguments. Neither can have an unmatched generic + // parameter. + let params = self.tcx.hir().body(fn_node.body_id()?).params; + let params = params.get(is_method as usize..params.len() - fn_decl.c_variadic as usize)?; + let fn_inputs = fn_decl.inputs.get(is_method as usize..)?; + debug_assert_eq!(params.len(), fn_inputs.len()); - let mut params: Vec<&hir::Param<'_>> = self - .tcx - .hir() - .body(fn_node.body_id()?) - .params - .into_iter() - .skip(if is_method { 1 } else { 0 }) - .collect(); - - // The surrounding code expects variadic functions to not have a parameter representing - // the "..." parameter. This is already true of the FnDecl but not of the body params, so - // we drop it if it exists. - - if fn_decl.c_variadic { - params.pop(); - } - - debug_assert_eq!(params.len(), generic_params.len()); Some( - generic_params + fn_inputs .into_iter() + .map(|param| { + if let hir::TyKind::Path(QPath::Resolved( + _, + &hir::Path { res: Res::Def(_, res_def_id), .. }, + )) = param.kind + { + generic_params.iter().find(|param| param.def_id.to_def_id() == res_def_id) + } else { + None + } + }) .zip(params) - .enumerate() - .map(|(a, (b, c))| (a, b, c)) .collect(), ) } From 2ea95f867012e91fa742e60b7b6424abf86c05cf Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 29 Jan 2025 13:11:17 -0700 Subject: [PATCH 11/28] rustdoc: clean up a bunch of ts-expected-error declarations in main This mostly consists of handling potentially-null input and adding more global functions to the list of globals. --- src/librustdoc/html/static/js/main.js | 264 ++++++++++----------- src/librustdoc/html/static/js/rustdoc.d.ts | 22 ++ src/librustdoc/html/static/js/search.js | 2 - src/librustdoc/html/static/js/storage.js | 16 +- 4 files changed, 156 insertions(+), 148 deletions(-) diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index ccf4002bb300d..4698375f6ccb2 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -196,15 +196,13 @@ function switchDisplayedElement(elemToDisplay) { removeClass(el, "hidden"); const mainHeading = elemToDisplay.querySelector(".main-heading"); - // @ts-expect-error - if (mainHeading && searchState.rustdocToolbar) { - // @ts-expect-error - if (searchState.rustdocToolbar.parentElement) { - // @ts-expect-error - searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar); + if (mainHeading && window.searchState.rustdocToolbar) { + if (window.searchState.rustdocToolbar.parentElement) { + window.searchState.rustdocToolbar.parentElement.removeChild( + window.searchState.rustdocToolbar, + ); } - // @ts-expect-error - mainHeading.appendChild(searchState.rustdocToolbar); + mainHeading.appendChild(window.searchState.rustdocToolbar); } } @@ -212,7 +210,12 @@ function browserSupportsHistoryApi() { return window.history && typeof window.history.pushState === "function"; } -// @ts-expect-error +/** + * Download CSS from the web without making it the active stylesheet. + * We use this in the settings popover so that you don't get FOUC when switching. + * + * @param {string} cssUrl + */ function preLoadCss(cssUrl) { // https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload const link = document.createElement("link"); @@ -225,7 +228,11 @@ function preLoadCss(cssUrl) { (function() { const isHelpPage = window.location.pathname.endsWith("/help.html"); - // @ts-expect-error + /** + * Run a JavaScript file asynchronously. + * @param {string} url + * @param {function(): any} errorCallback + */ function loadScript(url, errorCallback) { const script = document.createElement("script"); script.src = url; @@ -235,13 +242,12 @@ function preLoadCss(cssUrl) { document.head.append(script); } - if (getSettingsButton()) { - // @ts-expect-error - getSettingsButton().onclick = event => { + const settingsButton = getSettingsButton(); + if (settingsButton) { + settingsButton.onclick = event => { if (event.ctrlKey || event.altKey || event.metaKey) { return; } - // @ts-expect-error window.hideAllModals(false); addClass(getSettingsButton(), "rotate"); event.preventDefault(); @@ -470,7 +476,6 @@ function preLoadCss(cssUrl) { } return onEachLazy(implElem.parentElement.parentElement.querySelectorAll( `[id^="${assocId}"]`), - // @ts-expect-error item => { const numbered = /^(.+?)-([0-9]+)$/.exec(item.id); if (item.id === assocId || (numbered && numbered[1] === assocId)) { @@ -522,7 +527,6 @@ function preLoadCss(cssUrl) { ev.preventDefault(); // @ts-expect-error searchState.defocus(); - // @ts-expect-error window.hideAllModals(true); // true = reset focus for tooltips } @@ -687,7 +691,6 @@ function preLoadCss(cssUrl) { // // By the way, this is only used by and useful for traits implemented automatically // (like "Send" and "Sync"). - // @ts-expect-error onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => { const aliases = el.getAttribute("data-aliases"); if (!aliases) { @@ -740,7 +743,6 @@ function preLoadCss(cssUrl) { code.innerHTML = struct[TEXT_IDX]; addClass(code, "code-header"); - // @ts-expect-error onEachLazy(code.getElementsByTagName("a"), elem => { const href = elem.getAttribute("href"); @@ -886,7 +888,6 @@ function preLoadCss(cssUrl) { const template = document.createElement("template"); template.innerHTML = text; - // @ts-expect-error onEachLazy(template.content.querySelectorAll("a"), elem => { const href = elem.getAttribute("href"); @@ -894,7 +895,6 @@ function preLoadCss(cssUrl) { elem.setAttribute("href", window.rootPath + href); } }); - // @ts-expect-error onEachLazy(template.content.querySelectorAll("[id]"), el => { let i = 0; if (idMap.has(el.id)) { @@ -912,7 +912,6 @@ function preLoadCss(cssUrl) { const oldHref = `#${el.id}`; const newHref = `#${el.id}-${i}`; el.id = `${el.id}-${i}`; - // @ts-expect-error onEachLazy(template.content.querySelectorAll("a[href]"), link => { if (link.getAttribute("href") === oldHref) { link.href = newHref; @@ -933,7 +932,6 @@ function preLoadCss(cssUrl) { // @ts-expect-error sidebarTraitList.append(li); } else { - // @ts-expect-error onEachLazy(templateAssocItems, item => { let block = hasClass(item, "associatedtype") ? associatedTypes : ( hasClass(item, "associatedconstant") ? associatedConstants : ( @@ -1040,7 +1038,6 @@ function preLoadCss(cssUrl) { function expandAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); removeClass(innerToggle, "will-expand"); - // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) { e.open = true; @@ -1053,7 +1050,6 @@ function preLoadCss(cssUrl) { function collapseAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); addClass(innerToggle, "will-expand"); - // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (e.parentNode.id !== "implementations-list" || (!hasClass(e, "implementors-toggle") && @@ -1092,7 +1088,6 @@ function preLoadCss(cssUrl) { function setImplementorsTogglesOpen(id, open) { const list = document.getElementById(id); if (list !== null) { - // @ts-expect-error onEachLazy(list.getElementsByClassName("implementors-toggle"), e => { e.open = open; }); @@ -1104,7 +1099,6 @@ function preLoadCss(cssUrl) { setImplementorsTogglesOpen("blanket-implementations-list", false); } - // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) { e.open = true; @@ -1124,7 +1118,6 @@ function preLoadCss(cssUrl) { } onEachLazy(document.querySelectorAll( ":not(.scraped-example) > .example-wrap > pre:not(.example-line-numbers)", - // @ts-expect-error ), x => { const parent = x.parentNode; const line_numbers = parent.querySelectorAll(".example-line-numbers"); @@ -1145,7 +1138,6 @@ function preLoadCss(cssUrl) { // @ts-expect-error window.rustdoc_remove_line_numbers_from_examples = () => { - // @ts-expect-error onEachLazy(document.querySelectorAll(".example-wrap > .example-line-numbers"), x => { x.parentNode.removeChild(x); }); @@ -1157,16 +1149,13 @@ function preLoadCss(cssUrl) { } function showSidebar() { - // @ts-expect-error window.hideAllModals(false); const sidebar = document.getElementsByClassName("sidebar")[0]; - // @ts-expect-error addClass(sidebar, "shown"); } function hideSidebar() { const sidebar = document.getElementsByClassName("sidebar")[0]; - // @ts-expect-error removeClass(sidebar, "shown"); } @@ -1193,7 +1182,6 @@ function preLoadCss(cssUrl) { mainElem.addEventListener("click", hideSidebar); } - // @ts-expect-error onEachLazy(document.querySelectorAll("a[href^='#']"), el => { // For clicks on internal links ( tags with a hash property), we expand the section we're // jumping to *before* jumping there. We can't do this in onHashChange, because it changes @@ -1204,7 +1192,6 @@ function preLoadCss(cssUrl) { }); }); - // @ts-expect-error onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"), el => { // @ts-expect-error el.addEventListener("click", e => { @@ -1241,7 +1228,6 @@ function preLoadCss(cssUrl) { clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); return; } - // @ts-expect-error window.hideAllModals(false); const wrapper = document.createElement("div"); if (notable_ty) { @@ -1422,7 +1408,6 @@ function preLoadCss(cssUrl) { } } - // @ts-expect-error onEachLazy(document.getElementsByClassName("tooltip"), e => { e.onclick = () => { e.TOOLTIP_FORCE_VISIBLE = e.TOOLTIP_FORCE_VISIBLE ? false : true; @@ -1527,7 +1512,6 @@ function preLoadCss(cssUrl) { // @ts-expect-error !getSettingsButton().contains(event.relatedTarget) ) { - // @ts-expect-error window.hidePopoverMenus(); } } @@ -1626,10 +1610,8 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm * * Pass "true" to reset focus for tooltip popovers. */ - // @ts-expect-error window.hideAllModals = switchFocus => { hideSidebar(); - // @ts-expect-error window.hidePopoverMenus(); hideTooltip(switchFocus); }; @@ -1637,9 +1619,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm /** * Hide all the popover menus. */ - // @ts-expect-error window.hidePopoverMenus = () => { - // @ts-expect-error onEachLazy(document.querySelectorAll("rustdoc-toolbar .popover"), elem => { elem.style.display = "none"; }); @@ -1708,7 +1688,6 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm if (shouldShowHelp) { showHelp(); } else { - // @ts-expect-error window.hidePopoverMenus(); } }); @@ -1780,30 +1759,42 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm }); } - // Pointer capture. - // - // Resizing is a single-pointer gesture. Any secondary pointer is ignored - // @ts-expect-error + /** + * Pointer capture. + * + * Resizing is a single-pointer gesture. Any secondary pointer is ignored + * + * @type {null|number} + */ let currentPointerId = null; - // "Desired" sidebar size. - // - // This is stashed here for window resizing. If the sidebar gets - // shrunk to maintain BODY_MIN, and then the user grows the window again, - // it gets the sidebar to restore its size. - // @ts-expect-error + /** + * "Desired" sidebar size. + * + * This is stashed here for window resizing. If the sidebar gets + * shrunk to maintain BODY_MIN, and then the user grows the window again, + * it gets the sidebar to restore its size. + * + * @type {null|number} + */ let desiredSidebarSize = null; - // Sidebar resize debouncer. - // - // The sidebar itself is resized instantly, but the body HTML can be too - // big for that, causing reflow jank. To reduce this, we queue up a separate - // animation frame and throttle it. + /** + * Sidebar resize debouncer. + * + * The sidebar itself is resized instantly, but the body HTML can be too + * big for that, causing reflow jank. To reduce this, we queue up a separate + * animation frame and throttle it. + * + * @type {false|ReturnType} + */ let pendingSidebarResizingFrame = false; - // If this page has no sidebar at all, bail out. + /** @type {HTMLElement|null} */ const resizer = document.querySelector(".sidebar-resizer"); + /** @type {HTMLElement|null} */ const sidebar = document.querySelector(".sidebar"); + // If this page has no sidebar at all, bail out. if (!resizer || !sidebar) { return; } @@ -1820,11 +1811,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // from settings.js, which uses a separate function. It's done here because // the minimum sidebar size is rather uncomfortable, and it must pass // through that size when using the shrink-to-nothing gesture. - function hideSidebar() { + const hideSidebar = function() { if (isSrcPage) { - // @ts-expect-error window.rustdocCloseSourceSidebar(); - // @ts-expect-error updateLocalStorage("src-sidebar-width", null); // [RUSTDOCIMPL] CSS variable fast path // @@ -1837,22 +1826,17 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // // So, to clear it, we need to clear all three. document.documentElement.style.removeProperty("--src-sidebar-width"); - // @ts-expect-error sidebar.style.removeProperty("--src-sidebar-width"); - // @ts-expect-error resizer.style.removeProperty("--src-sidebar-width"); } else { addClass(document.documentElement, "hide-sidebar"); updateLocalStorage("hide-sidebar", "true"); - // @ts-expect-error updateLocalStorage("desktop-sidebar-width", null); document.documentElement.style.removeProperty("--desktop-sidebar-width"); - // @ts-expect-error sidebar.style.removeProperty("--desktop-sidebar-width"); - // @ts-expect-error resizer.style.removeProperty("--desktop-sidebar-width"); } - } + }; // Call this function to show the sidebar from the resize handle. // On docs pages, this can only happen if the user has grabbed the resize @@ -1860,15 +1844,14 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // the visible range without releasing it. You can, however, grab the // resize handle on a source page with the sidebar closed, because it // remains visible all the time on there. - function showSidebar() { + const showSidebar = function() { if (isSrcPage) { - // @ts-expect-error window.rustdocShowSourceSidebar(); } else { removeClass(document.documentElement, "hide-sidebar"); updateLocalStorage("hide-sidebar", "false"); } - } + }; /** * Call this to set the correct CSS variable and setting. @@ -1876,44 +1859,40 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm * * @param {number} size - CSS px width of the sidebar. */ - function changeSidebarSize(size) { + const changeSidebarSize = function(size) { if (isSrcPage) { - // @ts-expect-error - updateLocalStorage("src-sidebar-width", size); + updateLocalStorage("src-sidebar-width", size.toString()); // [RUSTDOCIMPL] CSS variable fast path // // While this property is set on the HTML element at load time, // because the sidebar isn't actually loaded yet, // we scope this update to the sidebar to avoid hitting a slow // path in WebKit. - // @ts-expect-error sidebar.style.setProperty("--src-sidebar-width", size + "px"); - // @ts-expect-error resizer.style.setProperty("--src-sidebar-width", size + "px"); } else { - // @ts-expect-error - updateLocalStorage("desktop-sidebar-width", size); - // @ts-expect-error + updateLocalStorage("desktop-sidebar-width", size.toString()); sidebar.style.setProperty("--desktop-sidebar-width", size + "px"); - // @ts-expect-error resizer.style.setProperty("--desktop-sidebar-width", size + "px"); } - } + }; // Check if the sidebar is hidden. Since src pages and doc pages have // different settings, this function has to check that. - function isSidebarHidden() { + const isSidebarHidden = function() { return isSrcPage ? !hasClass(document.documentElement, "src-sidebar-expanded") : hasClass(document.documentElement, "hide-sidebar"); - } + }; - // Respond to the resize handle event. - // This function enforces size constraints, and implements the - // shrink-to-nothing gesture based on thresholds defined above. - // @ts-expect-error - function resize(e) { - // @ts-expect-error + /** + * Respond to the resize handle event. + * This function enforces size constraints, and implements the + * shrink-to-nothing gesture based on thresholds defined above. + * + * @param {PointerEvent} e + */ + const resize = function(e) { if (currentPointerId === null || currentPointerId !== e.pointerId) { return; } @@ -1931,97 +1910,83 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm changeSidebarSize(constrainedPos); desiredSidebarSize = constrainedPos; if (pendingSidebarResizingFrame !== false) { - // @ts-expect-error clearTimeout(pendingSidebarResizingFrame); } - // @ts-expect-error pendingSidebarResizingFrame = setTimeout(() => { - // @ts-expect-error if (currentPointerId === null || pendingSidebarResizingFrame === false) { return; } pendingSidebarResizingFrame = false; document.documentElement.style.setProperty( "--resizing-sidebar-width", - // @ts-expect-error desiredSidebarSize + "px", ); }, 100); } - } + }; // Respond to the window resize event. window.addEventListener("resize", () => { if (window.innerWidth < RUSTDOC_MOBILE_BREAKPOINT) { return; } stopResize(); - // @ts-expect-error - if (desiredSidebarSize >= (window.innerWidth - BODY_MIN)) { + if (desiredSidebarSize !== null && desiredSidebarSize >= (window.innerWidth - BODY_MIN)) { changeSidebarSize(window.innerWidth - BODY_MIN); - // @ts-expect-error } else if (desiredSidebarSize !== null && desiredSidebarSize > SIDEBAR_MIN) { - // @ts-expect-error changeSidebarSize(desiredSidebarSize); } }); - // @ts-expect-error - function stopResize(e) { - // @ts-expect-error + + /** + * @param {PointerEvent=} e + */ + const stopResize = function(e) { if (currentPointerId === null) { return; } if (e) { e.preventDefault(); } - // @ts-expect-error desiredSidebarSize = sidebar.getBoundingClientRect().width; - // @ts-expect-error removeClass(resizer, "active"); window.removeEventListener("pointermove", resize, false); window.removeEventListener("pointerup", stopResize, false); removeClass(document.documentElement, "sidebar-resizing"); document.documentElement.style.removeProperty( "--resizing-sidebar-width"); - // @ts-expect-error if (resizer.releasePointerCapture) { - // @ts-expect-error resizer.releasePointerCapture(currentPointerId); currentPointerId = null; } - } - // @ts-expect-error - function initResize(e) { - // @ts-expect-error + }; + + /** + * @param {PointerEvent} e + */ + const initResize = function(e) { if (currentPointerId !== null || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { return; } - // @ts-expect-error if (resizer.setPointerCapture) { - // @ts-expect-error resizer.setPointerCapture(e.pointerId); - // @ts-expect-error if (!resizer.hasPointerCapture(e.pointerId)) { // unable to capture pointer; something else has it // on iOS, this usually means you long-clicked a link instead - // @ts-expect-error resizer.releasePointerCapture(e.pointerId); return; } currentPointerId = e.pointerId; } - // @ts-expect-error window.hideAllModals(false); e.preventDefault(); window.addEventListener("pointermove", resize, false); window.addEventListener("pointercancel", stopResize, false); window.addEventListener("pointerup", stopResize, false); - // @ts-expect-error addClass(resizer, "active"); addClass(document.documentElement, "sidebar-resizing"); - // @ts-expect-error const pos = e.clientX - sidebar.offsetLeft - 3; document.documentElement.style.setProperty( "--resizing-sidebar-width", pos + "px"); desiredSidebarSize = null; - } + }; resizer.addEventListener("pointerdown", initResize, false); }()); @@ -2029,8 +1994,13 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // and the copy buttons on the code examples. (function() { // Common functions to copy buttons. - // @ts-expect-error + /** + * @param {string|null} content + */ function copyContentToClipboard(content) { + if (content === null) { + return; + } const el = document.createElement("textarea"); el.value = content; el.setAttribute("readonly", ""); @@ -2044,15 +2014,17 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm document.body.removeChild(el); } - // @ts-expect-error + /** + * @param {HTMLElement & {reset_button_timeout?: ReturnType}} button + */ function copyButtonAnimation(button) { button.classList.add("clicked"); if (button.reset_button_timeout !== undefined) { - window.clearTimeout(button.reset_button_timeout); + clearTimeout(button.reset_button_timeout); } - button.reset_button_timeout = window.setTimeout(() => { + button.reset_button_timeout = setTimeout(() => { button.reset_button_timeout = undefined; button.classList.remove("clicked"); }, 1000); @@ -2067,9 +2039,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Most page titles are ' in - Rust', except // modules (which don't have the first part) and keywords/primitives // (which don't have a module path) - // @ts-expect-error - const title = document.querySelector("title").textContent.replace(" - Rust", ""); - const [item, module] = title.split(" in "); + const [item, module] = document.title.split(" in "); const path = [item]; if (module !== undefined) { path.unshift(module); @@ -2079,8 +2049,10 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm copyButtonAnimation(but); }; - // Copy buttons on code examples. - // @ts-expect-error + /** + * Copy buttons on code examples. + * @param {HTMLElement|null} codeElem + */ function copyCode(codeElem) { if (!codeElem) { // Should never happen, but the world is a dark and dangerous place. @@ -2089,23 +2061,34 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm copyContentToClipboard(codeElem.textContent); } - // @ts-expect-error + /** + * @param {UIEvent} event + * @returns {HTMLElement|null} + */ function getExampleWrap(event) { - let elem = event.target; - while (!hasClass(elem, "example-wrap")) { - if (elem === document.body || - elem.tagName === "A" || - elem.tagName === "BUTTON" || - hasClass(elem, "docblock") - ) { - return null; + const target = event.target; + if (target instanceof HTMLElement) { + /** @type {HTMLElement|null} */ + let elem = target; + while (elem !== null && !hasClass(elem, "example-wrap")) { + if (elem === document.body || + elem.tagName === "A" || + elem.tagName === "BUTTON" || + hasClass(elem, "docblock") + ) { + return null; + } + elem = elem.parentElement; } - elem = elem.parentElement; + return elem; + } else { + return null; } - return elem; } - // @ts-expect-error + /** + * @param {UIEvent} event + */ function addCopyButton(event) { const elem = getExampleWrap(event); if (elem === null) { @@ -2132,15 +2115,17 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm }); parent.appendChild(copyButton); - if (!elem.parentElement.classList.contains("scraped-example")) { + if (!elem.parentElement || !elem.parentElement.classList.contains("scraped-example") || + !window.updateScrapedExample) { return; } const scrapedWrapped = elem.parentElement; - // @ts-expect-error window.updateScrapedExample(scrapedWrapped, parent); } - // @ts-expect-error + /** + * @param {UIEvent} event + */ function showHideCodeExampleButtons(event) { const elem = getExampleWrap(event); if (elem === null) { @@ -2159,7 +2144,6 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm buttons.classList.toggle("keep-visible"); } - // @ts-expect-error onEachLazy(document.querySelectorAll(".docblock .example-wrap"), elem => { elem.addEventListener("mouseover", addCopyButton); elem.addEventListener("click", showHideCodeExampleButtons); diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 18a3e22113b8b..acea7828e86a9 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -20,6 +20,28 @@ declare global { * As a multi-page application, we know this never changes once set. */ currentCrate: string|null; + /** + * Hide popovers, tooltips, or the mobile sidebar. + */ + hideAllModals: function(boolean), + /** + * Hide popovers, but leave other modals alone. + */ + hidePopoverMenus: function(), + /** + * Hide the source page sidebar. If it's already closed, + * or if this is a docs page, this function does nothing. + */ + rustdocCloseSourceSidebar: function(), + /** + * Show the source page sidebar. If it's already opened, + * or if this is a docs page, this function does nothing. + */ + rustdocShowSourceSidebar: function(), + /** + * Set up event listeners for a scraped source example. + */ + updateScrapedExample?: function(HTMLElement, HTMLElement), } interface HTMLElement { /** Used by the popover tooltip code. */ diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 1ad32721e0687..f25f37bab59c2 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -4732,10 +4732,8 @@ function printTab(nb) { // Corrections only kick in on type-based searches. const correctionsElem = document.getElementsByClassName("search-corrections"); if (isTypeSearch) { - // @ts-expect-error removeClass(correctionsElem[0], "hidden"); } else { - // @ts-expect-error addClass(correctionsElem[0], "hidden"); } } else if (nb !== 0) { diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 10369e77320fe..3042373fb096f 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -59,7 +59,7 @@ function hasClass(elem, className) { * Add a class to a DOM Element. If `elem` is null, * does nothing. This function is idempotent. * - * @param {HTMLElement|null} elem + * @param {Element|null} elem * @param {string} className */ function addClass(elem, className) { @@ -72,7 +72,7 @@ function addClass(elem, className) { * Remove a class from a DOM Element. If `elem` is null, * does nothing. This function is idempotent. * - * @param {HTMLElement|null} elem + * @param {Element|null} elem * @param {string} className */ // eslint-disable-next-line no-unused-vars @@ -85,7 +85,7 @@ function removeClass(elem, className) { /** * Run a callback for every element of an Array. * @param {Array} arr - The array to iterate over - * @param {function(?): boolean|undefined} func - The callback + * @param {function(?): boolean|void} func - The callback */ function onEach(arr, func) { for (const elem of arr) { @@ -103,7 +103,7 @@ function onEach(arr, func) { * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection * https://developer.mozilla.org/en-US/docs/Web/API/NodeList * @param {NodeList|HTMLCollection} lazyArray - An array to iterate over - * @param {function(?): boolean} func - The callback + * @param {function(?): boolean|void} func - The callback */ // eslint-disable-next-line no-unused-vars function onEachLazy(lazyArray, func) { @@ -119,11 +119,15 @@ function onEachLazy(lazyArray, func) { * If localStorage is disabled, this function does nothing. * * @param {string} name - * @param {string} value + * @param {string|null} value */ function updateLocalStorage(name, value) { try { - window.localStorage.setItem("rustdoc-" + name, value); + if (value === null) { + window.localStorage.removeItem("rustdoc-" + name); + } else { + window.localStorage.setItem("rustdoc-" + name, value); + } } catch (e) { // localStorage is not accessible, do nothing } From 6378fbc366ad552ee791bcac670e0f3939489ef7 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 3 Feb 2025 11:19:17 -0500 Subject: [PATCH 12/28] Check for generic parameter mismatches on trait functions. --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 124 ++++++++++++------ tests/ui/fn/param-mismatch-trait-fn.rs | 10 ++ tests/ui/fn/param-mismatch-trait-fn.stderr | 23 ++++ tests/ui/methods/issues/issue-61525.stderr | 2 +- ...missing-associated-type-restriction.stderr | 2 +- tests/ui/traits/issue-52893.stderr | 2 +- 6 files changed, 120 insertions(+), 43 deletions(-) create mode 100644 tests/ui/fn/param-mismatch-trait-fn.rs create mode 100644 tests/ui/fn/param-mismatch-trait-fn.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 7ee246e07743c..e30c0e115dc2f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -21,7 +21,7 @@ use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::Session; -use rustc_span::{DUMMY_SP, Ident, Span, kw, sym}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext}; @@ -2414,11 +2414,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) { let Some(generic_param) = generic_param else { - spans.push_span_label(param.span, ""); + spans.push_span_label(param.span(), ""); continue; }; - let other_params_matched: Vec<(ExpectedIdx, &hir::Param<'_>)> = + let other_params_matched: Vec<(ExpectedIdx, FnParam<'_>)> = params_with_generics .iter_enumerated() .filter(|&(other_idx, &(other_generic_param, _))| { @@ -2447,9 +2447,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let other_param_matched_names: Vec = other_params_matched .iter() .map(|(idx, other_param)| { - if let hir::PatKind::Binding(_, _, ident, _) = other_param.pat.kind - { - format!("`{ident}`") + if let Some(name) = other_param.name() { + format!("`{name}`") } else { format!("parameter #{}", idx.as_u32() + 1) } @@ -2462,7 +2461,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if matched_inputs[idx].is_some() { spans.push_span_label( - param.span, + param.span(), format!( "{} need{} to match the {} type of this parameter", listify(&other_param_matched_names, |n| n.to_string()) @@ -2477,7 +2476,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } else { spans.push_span_label( - param.span, + param.span(), format!( "this parameter needs to match the {} type of {}", matched_ty, @@ -2488,7 +2487,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } generics_with_unmatched_params.push(generic_param); } else { - spans.push_span_label(param.span, ""); + spans.push_span_label(param.span(), ""); } } @@ -2515,8 +2514,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }) .map(|(idx, &(_, param))| { - if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind { - format!("`{ident}`") + if let Some(name) = param.name() { + format!("`{name}`") } else { format!("parameter #{}", idx.as_u32() + 1) } @@ -2673,35 +2672,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, def_id: DefId, is_method: bool, - ) -> Option>, &hir::Param<'_>)>> { - let fn_node = self.tcx.hir().get_if_local(def_id)?; - let fn_decl = fn_node.fn_decl()?; - let generic_params = fn_node.generics()?.params; - - // Remove both the receiver and variadic arguments. Neither can have an unmatched generic - // parameter. - let params = self.tcx.hir().body(fn_node.body_id()?).params; - let params = params.get(is_method as usize..params.len() - fn_decl.c_variadic as usize)?; - let fn_inputs = fn_decl.inputs.get(is_method as usize..)?; - debug_assert_eq!(params.len(), fn_inputs.len()); - - Some( - fn_inputs - .into_iter() - .map(|param| { - if let hir::TyKind::Path(QPath::Resolved( - _, - &hir::Path { res: Res::Def(_, res_def_id), .. }, - )) = param.kind - { - generic_params.iter().find(|param| param.def_id.to_def_id() == res_def_id) - } else { - None - } - }) - .zip(params) - .collect(), - ) + ) -> Option>, FnParam<'_>)>> { + let (sig, generics, body_id, param_names) = match self.tcx.hir().get_if_local(def_id)? { + hir::Node::TraitItem(&hir::TraitItem { + generics, + kind: hir::TraitItemKind::Fn(sig, trait_fn), + .. + }) => match trait_fn { + hir::TraitFn::Required(params) => (sig, generics, None, Some(params)), + hir::TraitFn::Provided(body) => (sig, generics, Some(body), None), + }, + hir::Node::ImplItem(&hir::ImplItem { + generics, + kind: hir::ImplItemKind::Fn(sig, body), + .. + }) + | hir::Node::Item(&hir::Item { + kind: hir::ItemKind::Fn { sig, generics, body, .. }, + .. + }) => (sig, generics, Some(body), None), + _ => return None, + }; + + // Make sure to remove both the receiver and variadic argument. Both are removed + // when matching parameter types. + let fn_inputs = sig.decl.inputs.get(is_method as usize..)?.iter().map(|param| { + if let hir::TyKind::Path(QPath::Resolved( + _, + &hir::Path { res: Res::Def(_, res_def_id), .. }, + )) = param.kind + { + generics.params.iter().find(|param| param.def_id.to_def_id() == res_def_id) + } else { + None + } + }); + match (body_id, param_names) { + (Some(_), Some(_)) | (None, None) => unreachable!(), + (Some(body), None) => { + let params = self.tcx.hir().body(body).params; + let params = + params.get(is_method as usize..params.len() - sig.decl.c_variadic as usize)?; + debug_assert_eq!(params.len(), fn_inputs.len()); + Some(fn_inputs.zip(params.iter().map(|param| FnParam::Param(param))).collect()) + } + (None, Some(params)) => { + let params = params.get(is_method as usize..)?; + debug_assert_eq!(params.len(), fn_inputs.len()); + Some(fn_inputs.zip(params.iter().map(|param| FnParam::Name(param))).collect()) + } + } } } @@ -2724,3 +2744,27 @@ impl<'tcx> Visitor<'tcx> for FindClosureArg<'tcx> { hir::intravisit::walk_expr(self, ex); } } + +#[derive(Clone, Copy)] +enum FnParam<'hir> { + Param(&'hir hir::Param<'hir>), + Name(&'hir Ident), +} +impl FnParam<'_> { + fn span(&self) -> Span { + match self { + Self::Param(x) => x.span, + Self::Name(x) => x.span, + } + } + + fn name(&self) -> Option { + match self { + Self::Param(x) if let hir::PatKind::Binding(_, _, ident, _) = x.pat.kind => { + Some(ident.name) + } + Self::Name(x) if x.name != kw::Empty => Some(x.name), + _ => None, + } + } +} diff --git a/tests/ui/fn/param-mismatch-trait-fn.rs b/tests/ui/fn/param-mismatch-trait-fn.rs new file mode 100644 index 0000000000000..69ded6a9068d7 --- /dev/null +++ b/tests/ui/fn/param-mismatch-trait-fn.rs @@ -0,0 +1,10 @@ +trait Foo { + fn same_type(_: T, _: T); +} + +fn f(x: X, y: Y) { + T::same_type([x], Some(y)); + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/fn/param-mismatch-trait-fn.stderr b/tests/ui/fn/param-mismatch-trait-fn.stderr new file mode 100644 index 0000000000000..28e1bcaaf49dc --- /dev/null +++ b/tests/ui/fn/param-mismatch-trait-fn.stderr @@ -0,0 +1,23 @@ +error[E0308]: mismatched types + --> $DIR/param-mismatch-trait-fn.rs:6:23 + | +LL | T::same_type([x], Some(y)); + | ------------ --- ^^^^^^^ expected `[X; 1]`, found `Option` + | | | + | | expected all arguments to be this `[X; 1]` type because they need to match the type of this parameter + | arguments to this function are incorrect + | + = note: expected array `[X; 1]` + found enum `Option` +note: associated function defined here + --> $DIR/param-mismatch-trait-fn.rs:2:8 + | +LL | fn same_type(_: T, _: T); + | ^^^^^^^^^ - - - this parameter needs to match the `[X; 1]` type of parameter #1 + | | | + | | parameter #2 needs to match the `[X; 1]` type of this parameter + | parameter #1 and parameter #2 both reference this parameter `T` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/methods/issues/issue-61525.stderr b/tests/ui/methods/issues/issue-61525.stderr index 35001ae22a6c7..7ac3d3dc0cff0 100644 --- a/tests/ui/methods/issues/issue-61525.stderr +++ b/tests/ui/methods/issues/issue-61525.stderr @@ -32,7 +32,7 @@ note: method defined here --> $DIR/issue-61525.rs:2:8 | LL | fn query(self, q: Q); - | ^^^^^ + | ^^^^^ - error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/trait-with-missing-associated-type-restriction.stderr b/tests/ui/suggestions/trait-with-missing-associated-type-restriction.stderr index 980c2455c8e41..df59a28c4b97b 100644 --- a/tests/ui/suggestions/trait-with-missing-associated-type-restriction.stderr +++ b/tests/ui/suggestions/trait-with-missing-associated-type-restriction.stderr @@ -94,7 +94,7 @@ note: method defined here --> $DIR/trait-with-missing-associated-type-restriction.rs:9:8 | LL | fn funk(&self, _: Self::A); - | ^^^^ + | ^^^^ - help: consider constraining the associated type `>::A` to `{integer}` | LL | fn bar2>(x: T) { diff --git a/tests/ui/traits/issue-52893.stderr b/tests/ui/traits/issue-52893.stderr index c37dde90e336f..3c5df82fcdc87 100644 --- a/tests/ui/traits/issue-52893.stderr +++ b/tests/ui/traits/issue-52893.stderr @@ -22,7 +22,7 @@ note: method defined here --> $DIR/issue-52893.rs:11:8 | LL | fn push(self, other: T) -> Self::PushRes; - | ^^^^ + | ^^^^ ----- error: aborting due to 1 previous error From 6b016d7e592798610f6313891cbc3ba81906f894 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Mon, 3 Feb 2025 19:18:07 +0000 Subject: [PATCH 13/28] Mark `std::fmt::from_fn` as `#[must_use]` --- library/core/src/fmt/builders.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 1862be0e86c5d..665b05b12ec07 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1228,6 +1228,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] +#[must_use = "returns a type implementing Debug and Display, which do not have any effects unless they are used"] pub fn from_fn) -> fmt::Result>(f: F) -> FromFn { FromFn(f) } From bcb8565f301b579dee60fffe87d5a329cc69fefa Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 2 Dec 2024 20:35:13 +0000 Subject: [PATCH 14/28] 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. --- compiler/rustc_borrowck/src/type_check/mod.rs | 1 + compiler/rustc_codegen_cranelift/src/base.rs | 9 ++++++ compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 4 +++ .../src/check_consts/check.rs | 6 +++- .../rustc_const_eval/src/interpret/machine.rs | 10 ++++++ .../src/interpret/operator.rs | 1 + compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_feature/src/unstable.rs | 2 ++ .../rustc_hir_analysis/src/check/intrinsic.rs | 20 ++++++++++++ compiler/rustc_middle/src/mir/pretty.rs | 1 + compiler/rustc_middle/src/mir/syntax.rs | 3 ++ compiler/rustc_middle/src/mir/tcx.rs | 3 +- .../src/move_paths/builder.rs | 6 +++- compiler/rustc_mir_transform/src/gvn.rs | 1 + .../src/known_panics_lint.rs | 1 + .../src/lower_intrinsics.rs | 11 +++++++ .../rustc_mir_transform/src/promote_consts.rs | 1 + compiler/rustc_mir_transform/src/validate.rs | 5 ++- compiler/rustc_session/src/config/cfg.rs | 7 ++++ compiler/rustc_session/src/options.rs | 2 ++ compiler/rustc_session/src/session.rs | 4 +++ .../rustc_smir/src/rustc_smir/convert/mir.rs | 1 + compiler/rustc_span/src/symbol.rs | 4 +++ compiler/stable_mir/src/mir/body.rs | 5 ++- library/core/src/intrinsics/mod.rs | 32 +++++++++++++++++++ .../clippy_utils/src/qualify_min_const_fn.rs | 2 +- src/tools/miri/src/machine.rs | 5 +++ tests/ui/contracts/contract-intrinsics.rs | 23 +++++++++++++ .../feature-gate-cfg-contract-checks.rs | 5 +++ .../feature-gate-cfg-contract-checks.stderr | 13 ++++++++ 30 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 tests/ui/contracts/contract-intrinsics.rs create mode 100644 tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs create mode 100644 tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 92492bfdb8d31..b727616ebceb9 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1650,6 +1650,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::SizedBound, ); } + &Rvalue::NullaryOp(NullOp::ContractChecks, _) => {} &Rvalue::NullaryOp(NullOp::UbChecks, _) => {} Rvalue::ShallowInitBox(operand, ty) => { diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 7a40d236b9288..de2ce1768fa18 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -874,6 +874,15 @@ fn codegen_stmt<'tcx>( lval.write_cvalue(fx, val); 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( fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(val).unwrap()), diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 85de3238b3e77..27cb7883b9a6c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -741,6 +741,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = bx.tcx().sess.ub_checks(); 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(); OperandRef { diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index e8052a3c83a1f..d0ce027ec2b71 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -675,7 +675,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Cast(_, _, _) => {} Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks, + NullOp::SizeOf + | NullOp::AlignOf + | NullOp::OffsetOf(_) + | NullOp::UbChecks + | NullOp::ContractChecks, _, ) => {} Rvalue::ShallowInitBox(_, _) => {} diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 8f6b15b8df012..1a799f5dea5b5 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -293,6 +293,9 @@ pub trait Machine<'tcx>: Sized { /// Determines the result of a `NullaryOp::UbChecks` invocation. 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. /// You can use this to detect long or endlessly running programs. #[inline] @@ -679,6 +682,13 @@ pub macro compile_time_machine(<$tcx: lifetime>) { 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)] fn adjust_global_allocation<'b>( _ecx: &InterpCx<$tcx, Self>, diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 5fa632fc57aaf..899670aeb62da 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -537,6 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ImmTy::from_uint(val, usize_layout()) } UbChecks => ImmTy::from_bool(M::ub_checks(self)?, *self.tcx), + ContractChecks => ImmTy::from_bool(M::contract_checks(self)?, *self.tcx), }) } } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index e0543977e98d8..67eb96e4d56ad 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -19,6 +19,7 @@ const GATED_CFGS: &[GatedCfg] = &[ // (name in cfg, feature, function to check if the feature is enabled) (sym::overflow_checks, sym::cfg_overflow_checks, Features::cfg_overflow_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_has_atomic_equal_alignment, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 1a216ebf117c6..08a5e22db3a54 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -403,6 +403,8 @@ declare_features! ( (unstable, c_variadic, "1.34.0", Some(44930)), /// Allows the use of `#[cfg()]`. (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. (unstable, cfg_overflow_checks, "1.71.0", Some(111466)), /// Provides the relocation model information as cfg entry diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index cf3d489730425..6c0cebccefd95 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -132,6 +132,9 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::aggregate_raw_ptr | sym::ptr_metadata | sym::ub_checks + | sym::contract_checks + | sym::contract_check_requires + | sym::contract_check_ensures | sym::fadd_algebraic | sym::fsub_algebraic | sym::fmul_algebraic @@ -219,6 +222,18 @@ pub fn check_intrinsic_type( } }; (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 { let safety = intrinsic_operation_unsafety(tcx, intrinsic_id); 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))), + // contract_checks() -> bool + sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool), + // contract_check_requires::(C) -> bool, where C: impl Fn() -> bool + sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.bool), + sym::simd_eq | sym::simd_ne | sym::simd_lt diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 09d7e60e19941..f53b4f3def259 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1103,6 +1103,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { NullOp::AlignOf => write!(fmt, "AlignOf({t})"), NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"), NullOp::UbChecks => write!(fmt, "UbChecks()"), + NullOp::ContractChecks => write!(fmt, "ContractChecks()"), } } ThreadLocalRef(did) => ty::tls::with(|tcx| { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 2da25f480c65f..9cec8d832dd14 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1591,6 +1591,9 @@ pub enum NullOp<'tcx> { /// Returns whether we should perform some UB-checking at runtime. /// See the `ub_checks` intrinsic docs for details. 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)] diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 49449426fa408..af23c8b2ea76d 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -230,7 +230,8 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { 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 { AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64), AggregateKind::Tuple => { diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 6e00e427a46c7..b6c259aa4e0ab 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -417,7 +417,11 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | Rvalue::Discriminant(..) | Rvalue::Len(..) | Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbChecks, + NullOp::SizeOf + | NullOp::AlignOf + | NullOp::OffsetOf(..) + | NullOp::UbChecks + | NullOp::ContractChecks, _, ) => {} } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index c261e25100d38..d2ffd26f0a06d 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -545,6 +545,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { .offset_of_subfield(self.typing_env(), layout, fields.iter()) .bytes(), NullOp::UbChecks => return None, + NullOp::ContractChecks => return None, }; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); let imm = ImmTy::from_uint(val, usize_layout); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 2864cc0b9fe0c..e43254ba089e5 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -629,6 +629,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { .offset_of_subfield(self.typing_env, op_layout, fields.iter()) .bytes(), NullOp::UbChecks => return None, + NullOp::ContractChecks => return None, }; ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into() } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 9a9f66ed4fd7a..9c21bcfc0d26a 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -34,6 +34,17 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { }); 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 => { let target = target.unwrap(); block.statements.push(Statement { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 9101c9bfc9aeb..4dbbcae1756b7 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -457,6 +457,7 @@ impl<'tcx> Validator<'_, 'tcx> { NullOp::AlignOf => {} NullOp::OffsetOf(_) => {} NullOp::UbChecks => {} + NullOp::ContractChecks => {} }, Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable), diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index e282eaf761c10..b7a3770fc6b1d 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1379,7 +1379,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Rvalue::Repeat(_, _) | Rvalue::ThreadLocalRef(_) | Rvalue::RawPtr(_, _) - | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks, _) + | Rvalue::NullaryOp( + NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks | NullOp::ContractChecks, + _, + ) | Rvalue::Discriminant(_) => {} Rvalue::WrapUnsafeBinder(op, ty) => { diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index d586f913335e0..52920e0372e2b 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -119,6 +119,7 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { (sym::overflow_checks, None) => disallow(cfg, "-C overflow-checks"), (sym::debug_assertions, None) => disallow(cfg, "-C debug-assertions"), (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::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 { ins_none!(sym::emscripten_wasm_eh); } + + if sess.contract_checks() { + ins_none!(sym::contract_checks); + } + ret } @@ -464,6 +470,7 @@ impl CheckCfg { ins!(sym::target_thread_local, no_values); ins!(sym::ub_checks, no_values); + ins!(sym::contract_checks, no_values); ins!(sym::unix, no_values); ins!(sym::windows, no_values); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 4ed5499d32c6d..35819f896c5bd 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2114,6 +2114,8 @@ options! { "the backend to use"), combine_cgu: bool = (false, parse_bool, [TRACKED], "combine CGUs into a single one"), + contract_checks: Option = (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], "control details of coverage instrumentation"), crate_attr: Vec = (Vec::new(), parse_string_push, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 2b79081a26e72..c9bb42bdfa1ec 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -709,6 +709,10 @@ impl Session { 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 { self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model) } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 4a0420cc60311..bdd6e16a7c171 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -291,6 +291,7 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> { indices.iter().map(|idx| idx.stable(tables)).collect(), ), UbChecks => stable_mir::mir::NullOp::UbChecks, + ContractChecks => stable_mir::mir::NullOp::ContractChecks, } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b23cc90991154..3138efdfd9b0d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -566,6 +566,7 @@ symbols! { cfg_attr, cfg_attr_multi, cfg_boolean_literals, + cfg_contract_checks, cfg_doctest, cfg_emscripten_wasm_eh, cfg_eval, @@ -675,6 +676,9 @@ symbols! { const_ty_placeholder: "", constant, constructor, + contract_check_ensures, + contract_check_requires, + contract_checks, convert_identity, copy, copy_closures, diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index eec6cd8d49ba3..a6406e9db8e35 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -608,7 +608,8 @@ impl Rvalue { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { 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 { AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), AggregateKind::Tuple => Ok(Ty::new_tuple( @@ -1007,6 +1008,8 @@ pub enum NullOp { OffsetOf(Vec<(VariantIdx, FieldIdx)>), /// cfg!(ub_checks), but at codegen time UbChecks, + /// cfg!(contract_checks), but at codegen time + ContractChecks, } impl Operand { diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index bf07632d9928b..beea0996775a4 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -4044,6 +4044,38 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) // 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 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. /// /// # Safety diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 0aaef91e48a6d..5a3a3d0cedc42 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -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::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 4735db48e81f8..6bd1076a8a848 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1150,6 +1150,11 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { 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)] fn thread_local_static_pointer( ecx: &mut MiriInterpCx<'tcx>, diff --git a/tests/ui/contracts/contract-intrinsics.rs b/tests/ui/contracts/contract-intrinsics.rs new file mode 100644 index 0000000000000..6e3565baf7ac5 --- /dev/null +++ b/tests/ui/contracts/contract-intrinsics.rs @@ -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); +} diff --git a/tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs new file mode 100644 index 0000000000000..cd9bf12b5e773 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs @@ -0,0 +1,5 @@ +#![crate_type = "lib"] + +pub fn contract_checks_are_enabled() -> bool { + cfg!(contract_checks) //~ ERROR `cfg(contract_checks)` is experimental +} diff --git a/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr new file mode 100644 index 0000000000000..af4e605e57098 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr @@ -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 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`. From 777def87d58ee067e1df2e94a99fc099bcb15189 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 2 Dec 2024 21:16:35 +0000 Subject: [PATCH 15/28] contracts: added lang items that act as hooks for rustc-injected code to invoke. see test for an example of the kind of injected code that is anticipated here. --- compiler/rustc_hir/src/lang_items.rs | 4 ++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/contracts.rs | 33 ++++++++++++++++ library/core/src/lib.rs | 5 +++ tests/ui/contracts/contract-lang-items.rs | 47 +++++++++++++++++++++++ 5 files changed, 90 insertions(+) create mode 100644 library/core/src/contracts.rs create mode 100644 tests/ui/contracts/contract-lang-items.rs diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index d9759580e8fdc..75898cbec14b8 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -418,6 +418,10 @@ language_item_table! { String, sym::String, string, Target::Struct, GenericRequirement::None; CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None; + + // Experimental lang items for implementing contract pre- and post-condition checking. + ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None; + ContractCheckRequires, sym::contract_check_requires, contract_check_requires_fn, Target::Fn, GenericRequirement::None; } pub enum GenericRequirement { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3138efdfd9b0d..df424b3312a0f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -676,6 +676,7 @@ symbols! { const_ty_placeholder: "", constant, constructor, + contract_build_check_ensures, contract_check_ensures, contract_check_requires, contract_checks, diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs new file mode 100644 index 0000000000000..c13565d59d58f --- /dev/null +++ b/library/core/src/contracts.rs @@ -0,0 +1,33 @@ +//! Unstable module containing the unstable contracts lang items and attribute macros. + +/// Emitted by rustc as a desugaring of `#[requires(PRED)] fn foo(x: X) { ... }` +/// into: `fn foo(x: X) { check_requires(|| PRED) ... }` +#[cfg(not(bootstrap))] +#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[lang = "contract_check_requires"] +#[track_caller] +pub fn check_requires bool>(c: C) { + if core::intrinsics::contract_checks() { + assert!(core::intrinsics::contract_check_requires(c), "failed requires check"); + } +} + +/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }` +/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }` +/// (including the implicit return of the tail expression, if any). +#[cfg(not(bootstrap))] +#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[lang = "contract_build_check_ensures"] +#[track_caller] +pub fn build_check_ensures(c: C) -> impl (FnOnce(Ret) -> Ret) + Copy +where + C: for<'a> FnOnce(&'a Ret) -> bool + Copy + 'static, +{ + #[track_caller] + move |ret| { + if core::intrinsics::contract_checks() { + assert!(core::intrinsics::contract_check_ensures(&ret, c), "failed ensures check"); + } + ret + } +} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index c18e0405f7293..fd283fe92fa36 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -114,6 +114,7 @@ #![feature(bstr)] #![feature(bstr_internals)] #![feature(const_carrying_mul_add)] +#![feature(closure_track_caller)] #![feature(const_eval_select)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] @@ -246,6 +247,10 @@ pub mod autodiff { pub use crate::macros::builtin::autodiff; } +#[cfg(not(bootstrap))] +#[unstable(feature = "rustc_contracts", issue = "none")] +pub mod contracts; + #[unstable(feature = "cfg_match", issue = "115585")] pub use crate::macros::cfg_match; diff --git a/tests/ui/contracts/contract-lang-items.rs b/tests/ui/contracts/contract-lang-items.rs new file mode 100644 index 0000000000000..1dbf71722fd70 --- /dev/null +++ b/tests/ui/contracts/contract-lang-items.rs @@ -0,0 +1,47 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +fn foo(x: Baz) -> i32 { + core::contracts::check_requires(|| x.baz > 0); + + let injected_checker = { + core::contracts::build_check_ensures(|ret| *ret > 100) + }; + + let ret = x.baz + 50; + injected_checker(ret) +} + +struct Baz { baz: i32 } + + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(foo(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + foo(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + foo(BAZ_FAIL_POST); +} From 38eff16d0aa029706a0b5845961f9b5ccddfd999 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 8 Jan 2025 16:38:25 -0800 Subject: [PATCH 16/28] Express contracts as part of function header and lower it to the contract lang items includes post-developed commit: do not suggest internal-only keywords as corrections to parse failures. includes post-developed commit: removed tabs that creeped in into rustfmt tool source code. includes post-developed commit, placating rustfmt self dogfooding. includes post-developed commit: add backquotes to prevent markdown checking from trying to treat an attr as a markdown hyperlink/ includes post-developed commit: fix lowering to keep contracts from being erroneously inherited by nested bodies (like closures). Rebase Conflicts: - compiler/rustc_parse/src/parser/diagnostics.rs - compiler/rustc_parse/src/parser/item.rs - compiler/rustc_span/src/hygiene.rs Remove contracts keywords from diagnostic messages --- compiler/rustc_ast/src/ast.rs | 9 +- compiler/rustc_ast/src/mut_visit.rs | 19 ++- compiler/rustc_ast/src/visit.rs | 17 ++- compiler/rustc_ast_lowering/src/expr.rs | 15 ++- compiler/rustc_ast_lowering/src/item.rs | 112 +++++++++++++++++- compiler/rustc_ast_lowering/src/lib.rs | 20 ++++ .../rustc_ast_passes/src/ast_validation.rs | 2 +- .../rustc_ast_pretty/src/pprust/state/item.rs | 21 +++- .../src/alloc_error_handler.rs | 1 + .../src/deriving/generic/mod.rs | 1 + .../src/global_allocator.rs | 1 + .../rustc_builtin_macros/src/test_harness.rs | 1 + compiler/rustc_hir/src/hir.rs | 2 + compiler/rustc_hir_typeck/src/expr.rs | 2 + compiler/rustc_parse/src/parser/generics.rs | 22 ++++ compiler/rustc_parse/src/parser/item.rs | 13 +- compiler/rustc_parse/src/parser/token_type.rs | 8 ++ compiler/rustc_resolve/src/def_collector.rs | 5 +- compiler/rustc_resolve/src/late.rs | 6 +- compiler/rustc_span/src/hygiene.rs | 3 + compiler/rustc_span/src/symbol.rs | 2 + library/core/src/lib.rs | 2 +- .../clippy/clippy_utils/src/ast_utils/mod.rs | 20 ++++ .../contracts/contract-ast-extensions-nest.rs | 44 +++++++ .../contracts/contract-ast-extensions-tail.rs | 42 +++++++ ...g-ensures-is-not-inherited-when-nesting.rs | 15 +++ ...-requires-is-not-inherited-when-nesting.rs | 17 +++ 27 files changed, 405 insertions(+), 17 deletions(-) create mode 100644 tests/ui/contracts/contract-ast-extensions-nest.rs create mode 100644 tests/ui/contracts/contract-ast-extensions-tail.rs create mode 100644 tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs create mode 100644 tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index b6f331d316cc9..08621c1c56a14 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3348,11 +3348,18 @@ pub struct Impl { pub items: ThinVec>, } +#[derive(Clone, Encodable, Decodable, Debug, Default)] +pub struct FnContract { + pub requires: Option>, + pub ensures: Option>, +} + #[derive(Clone, Encodable, Decodable, Debug)] pub struct Fn { pub defaultness: Defaultness, pub generics: Generics, pub sig: FnSig, + pub contract: Option>, pub body: Option>, } @@ -3650,7 +3657,7 @@ mod size_asserts { static_assert_size!(Block, 32); static_assert_size!(Expr, 72); static_assert_size!(ExprKind, 40); - static_assert_size!(Fn, 160); + static_assert_size!(Fn, 168); static_assert_size!(ForeignItem, 88); static_assert_size!(ForeignItemKind, 16); static_assert_size!(GenericArg, 24); diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 7caf7c4c35687..70616fe87691b 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -143,6 +143,10 @@ pub trait MutVisitor: Sized { walk_flat_map_assoc_item(self, i, ctxt) } + fn visit_contract(&mut self, c: &mut P) { + walk_contract(self, c); + } + fn visit_fn_decl(&mut self, d: &mut P) { walk_fn_decl(self, d); } @@ -958,13 +962,16 @@ fn walk_fn(vis: &mut T, kind: FnKind<'_>) { _ctxt, _ident, _vis, - Fn { defaultness, generics, body, sig: FnSig { header, decl, span } }, + Fn { defaultness, generics, contract, body, sig: FnSig { header, decl, span } }, ) => { // Identifier and visibility are visited as a part of the item. visit_defaultness(vis, defaultness); vis.visit_fn_header(header); vis.visit_generics(generics); vis.visit_fn_decl(decl); + if let Some(contract) = contract { + vis.visit_contract(contract); + } if let Some(body) = body { vis.visit_block(body); } @@ -979,6 +986,16 @@ fn walk_fn(vis: &mut T, kind: FnKind<'_>) { } } +fn walk_contract(vis: &mut T, contract: &mut P) { + let FnContract { requires, ensures } = contract.deref_mut(); + if let Some(pred) = requires { + vis.visit_expr(pred); + } + if let Some(pred) = ensures { + vis.visit_expr(pred); + } +} + fn walk_fn_decl(vis: &mut T, decl: &mut P) { let FnDecl { inputs, output } = decl.deref_mut(); inputs.flat_map_in_place(|param| vis.flat_map_param(param)); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 232fd546de9a3..714b074f930c2 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -188,6 +188,9 @@ pub trait Visitor<'ast>: Sized { fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) -> Self::Result { walk_closure_binder(self, b) } + fn visit_contract(&mut self, c: &'ast FnContract) -> Self::Result { + walk_contract(self, c) + } fn visit_where_predicate(&mut self, p: &'ast WherePredicate) -> Self::Result { walk_where_predicate(self, p) } @@ -800,6 +803,17 @@ pub fn walk_closure_binder<'a, V: Visitor<'a>>( V::Result::output() } +pub fn walk_contract<'a, V: Visitor<'a>>(visitor: &mut V, c: &'a FnContract) -> V::Result { + let FnContract { requires, ensures } = c; + if let Some(pred) = requires { + visitor.visit_expr(pred); + } + if let Some(pred) = ensures { + visitor.visit_expr(pred); + } + V::Result::output() +} + pub fn walk_where_predicate<'a, V: Visitor<'a>>( visitor: &mut V, predicate: &'a WherePredicate, @@ -862,12 +876,13 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu _ctxt, _ident, _vis, - Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, body }, + Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, contract, body }, ) => { // Identifier and visibility are visited as a part of the item. try_visit!(visitor.visit_fn_header(header)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_fn_decl(decl)); + visit_opt!(visitor, visit_contract, contract); visit_opt!(visitor, visit_block, body); } FnKind::Closure(binder, coroutine_kind, decl, body) => { diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 1267281f73ebe..19433047595a0 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -314,7 +314,20 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Continue(self.lower_jump_destination(e.id, *opt_label)) } ExprKind::Ret(e) => { - let e = e.as_ref().map(|x| self.lower_expr(x)); + let mut e = e.as_ref().map(|x| self.lower_expr(x)); + if let Some(Some((span, fresh_ident))) = self + .contract + .as_ref() + .map(|c| c.ensures.as_ref().map(|e| (e.expr.span, e.fresh_ident))) + { + let checker_fn = self.expr_ident(span, fresh_ident.0, fresh_ident.2); + let args = if let Some(e) = e { + std::slice::from_ref(e) + } else { + std::slice::from_ref(self.expr_unit(span)) + }; + e = Some(self.expr_call(span, checker_fn, args)); + } hir::ExprKind::Ret(e) } ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()), diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 74870d741504c..4679ccdddbbe1 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -207,9 +207,42 @@ impl<'hir> LoweringContext<'_, 'hir> { sig: FnSig { decl, header, span: fn_sig_span }, generics, body, + contract, .. }) => { self.with_new_scopes(*fn_sig_span, |this| { + assert!(this.contract.is_none()); + if let Some(contract) = contract { + let requires = contract.requires.clone(); + let ensures = contract.ensures.clone(); + let ensures = if let Some(ens) = ensures { + // FIXME: this needs to be a fresh (or illegal) identifier to prevent + // accidental capture of a parameter or global variable. + let checker_ident: Ident = + Ident::from_str_and_span("__ensures_checker", ens.span); + let (checker_pat, checker_hir_id) = this.pat_ident_binding_mode_mut( + ens.span, + checker_ident, + hir::BindingMode::NONE, + ); + + Some(crate::FnContractLoweringEnsures { + expr: ens, + fresh_ident: (checker_ident, checker_pat, checker_hir_id), + }) + } else { + None + }; + + // Note: `with_new_scopes` will reinstall the outer + // item's contract (if any) after its callback finishes. + this.contract.replace(crate::FnContractLoweringInfo { + span, + requires, + ensures, + }); + } + // Note: we don't need to change the return type from `T` to // `impl Future` here because lower_body // only cares about the input argument patterns in the function @@ -1054,10 +1087,81 @@ impl<'hir> LoweringContext<'_, 'hir> { body: impl FnOnce(&mut Self) -> hir::Expr<'hir>, ) -> hir::BodyId { self.lower_body(|this| { - ( - this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))), - body(this), - ) + let params = + this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))); + let result = body(this); + + let opt_contract = this.contract.take(); + + // { body } + // ==> + // { rustc_contract_requires(PRECOND); { body } } + let result: hir::Expr<'hir> = if let Some(contract) = opt_contract { + let lit_unit = |this: &mut LoweringContext<'_, 'hir>| { + this.expr(contract.span, hir::ExprKind::Tup(&[])) + }; + + let precond: hir::Stmt<'hir> = if let Some(req) = contract.requires { + let lowered_req = this.lower_expr_mut(&req); + let precond = this.expr_call_lang_item_fn_mut( + req.span, + hir::LangItem::ContractCheckRequires, + &*arena_vec![this; lowered_req], + ); + this.stmt_expr(req.span, precond) + } else { + let u = lit_unit(this); + this.stmt_expr(contract.span, u) + }; + + let (postcond_checker, result) = if let Some(ens) = contract.ensures { + let crate::FnContractLoweringEnsures { expr: ens, fresh_ident } = ens; + let lowered_ens: hir::Expr<'hir> = this.lower_expr_mut(&ens); + let postcond_checker = this.expr_call_lang_item_fn( + ens.span, + hir::LangItem::ContractBuildCheckEnsures, + &*arena_vec![this; lowered_ens], + ); + let checker_binding_pat = fresh_ident.1; + ( + this.stmt_let_pat( + None, + ens.span, + Some(postcond_checker), + this.arena.alloc(checker_binding_pat), + hir::LocalSource::Contract, + ), + { + let checker_fn = + this.expr_ident(ens.span, fresh_ident.0, fresh_ident.2); + let span = this.mark_span_with_reason( + DesugaringKind::Contract, + ens.span, + None, + ); + this.expr_call_mut( + span, + checker_fn, + std::slice::from_ref(this.arena.alloc(result)), + ) + }, + ) + } else { + let u = lit_unit(this); + (this.stmt_expr(contract.span, u), result) + }; + + let block = this.block_all( + contract.span, + arena_vec![this; precond, postcond_checker], + Some(this.arena.alloc(result)), + ); + this.expr_block(block) + } else { + result + }; + + (params, result) }) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 893da93085533..2715b3d621522 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -86,6 +86,19 @@ mod path; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } +#[derive(Debug, Clone)] +struct FnContractLoweringInfo<'hir> { + pub span: Span, + pub requires: Option>, + pub ensures: Option>, +} + +#[derive(Debug, Clone)] +struct FnContractLoweringEnsures<'hir> { + expr: ast::ptr::P, + fresh_ident: (Ident, hir::Pat<'hir>, HirId), +} + struct LoweringContext<'a, 'hir> { tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering, @@ -100,6 +113,8 @@ struct LoweringContext<'a, 'hir> { /// Collect items that were created by lowering the current owner. children: Vec<(LocalDefId, hir::MaybeOwner<'hir>)>, + contract: Option>, + coroutine_kind: Option, /// When inside an `async` context, this is the `HirId` of the @@ -148,6 +163,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bodies: Vec::new(), attrs: SortedMap::default(), children: Vec::default(), + contract: None, current_hir_id_owner: hir::CRATE_OWNER_ID, item_local_id_counter: hir::ItemLocalId::ZERO, ident_and_label_to_local_id: Default::default(), @@ -834,12 +850,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let was_in_loop_condition = self.is_in_loop_condition; self.is_in_loop_condition = false; + let old_contract = self.contract.take(); + let catch_scope = self.catch_scope.take(); let loop_scope = self.loop_scope.take(); let ret = f(self); self.catch_scope = catch_scope; self.loop_scope = loop_scope; + self.contract = old_contract; + self.is_in_loop_condition = was_in_loop_condition; self.current_item = current_item; diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index ea1f4a6559ac8..0049c5b4823cb 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -917,7 +917,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { walk_list!(self, visit_attribute, &item.attrs); return; // Avoid visiting again. } - ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, body }) => { + ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, contract: _, body }) => { self.check_defaultness(item.span, *defaultness); let is_intrinsic = diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 4cfcaa95233da..c10b5ad34e102 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -650,13 +650,17 @@ impl<'a> State<'a> { attrs: &[ast::Attribute], func: &ast::Fn, ) { - let ast::Fn { defaultness, generics, sig, body } = func; + let ast::Fn { defaultness, generics, sig, contract, body } = func; if body.is_some() { self.head(""); } self.print_visibility(vis); self.print_defaultness(*defaultness); self.print_fn(&sig.decl, sig.header, Some(name), generics); + if let Some(contract) = &contract { + self.nbsp(); + self.print_contract(contract); + } if let Some(body) = body { self.nbsp(); self.print_block_with_attrs(body, attrs); @@ -665,6 +669,21 @@ impl<'a> State<'a> { } } + fn print_contract(&mut self, contract: &ast::FnContract) { + if let Some(pred) = &contract.requires { + self.word("rustc_requires"); + self.popen(); + self.print_expr(pred, FixupContext::default()); + self.pclose(); + } + if let Some(pred) = &contract.ensures { + self.word("rustc_ensures"); + self.popen(); + self.print_expr(pred, FixupContext::default()); + self.pclose(); + } + } + pub(crate) fn print_fn( &mut self, decl: &ast::FnDecl, diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index d2b4e1ca824fd..cffc497860133 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -85,6 +85,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span defaultness: ast::Defaultness::Final, sig, generics: Generics::default(), + contract: None, body, })); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 755a733286cea..0631c5a80fc5e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -1034,6 +1034,7 @@ impl<'a> MethodDef<'a> { defaultness, sig, generics: fn_generics, + contract: None, body: Some(body_block), })), tokens: None, diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 8388e9dcafb8c..8fdbbf8e704a9 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -81,6 +81,7 @@ impl AllocFnFactory<'_, '_> { defaultness: ast::Defaultness::Final, sig, generics: Generics::default(), + contract: None, body, })); let item = self.cx.item( diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 31b068bd33dae..472e16e62d5b0 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -344,6 +344,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { defaultness, sig, generics: ast::Generics::default(), + contract: None, body: Some(main_body), })); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index af2f86b67e007..8bc09f631cf1e 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2598,6 +2598,8 @@ pub enum LocalSource { /// A desugared `expr = expr`, where the LHS is a tuple, struct, array or underscore expression. /// The span is that of the `=` sign. AssignDesugar(Span), + /// A contract `#[ensures(..)]` attribute injects a let binding for the check that runs at point of return. + Contract, } /// Hints at the original code for a `match _ { .. }`. diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index a519e177fbccd..f7bc21cc5260d 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -269,6 +269,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // diverging expression (e.g. it arose from desugaring of `try { return }`), // we skip issuing a warning because it is autogenerated code. ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {} + // Likewise, do not lint unreachable code injected via contracts desugaring. + ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::Contract) => {} ExprKind::Call(callee, _) => self.warn_if_unreachable(expr.hir_id, callee.span, "call"), ExprKind::MethodCall(segment, ..) => { self.warn_if_unreachable(expr.hir_id, segment.ident.span, "call") diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index b1b84b0b7011e..d5ac8d1588d7a 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -297,6 +297,28 @@ impl<'a> Parser<'a> { }) } + /// Parses a rustc-internal fn contract + /// (`rustc_contract_requires(WWW) rustc_contract_ensures(ZZZ)`) + pub(super) fn parse_contract( + &mut self, + ) -> PResult<'a, Option>> { + let requires = if self.eat_keyword_noexpect(exp!(RustcContractRequires).kw) { + Some(self.parse_expr()?) + } else { + None + }; + let ensures = if self.eat_keyword_noexpect(exp!(RustcContractEnsures).kw) { + Some(self.parse_expr()?) + } else { + None + }; + if requires.is_none() && ensures.is_none() { + Ok(None) + } else { + Ok(Some(rustc_ast::ptr::P(ast::FnContract { requires, ensures }))) + } + } + /// Parses an optional where-clause. /// /// ```ignore (only-for-syntax-highlight) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index f3e56be9f6e8b..dbdc31f06a29d 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -213,9 +213,12 @@ impl<'a> Parser<'a> { self.parse_use_item()? } else if self.check_fn_front_matter(check_pub, case) { // FUNCTION ITEM - let (ident, sig, generics, body) = + let (ident, sig, generics, contract, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis, case)?; - (ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body }))) + ( + ident, + ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, contract, body })), + ) } else if self.eat_keyword(exp!(Extern)) { if self.eat_keyword(exp!(Crate)) { // EXTERN CRATE @@ -2372,7 +2375,7 @@ impl<'a> Parser<'a> { sig_lo: Span, vis: &Visibility, case: Case, - ) -> PResult<'a, (Ident, FnSig, Generics, Option>)> { + ) -> PResult<'a, (Ident, FnSig, Generics, Option>, Option>)> { let fn_span = self.token.span; let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn` let ident = self.parse_ident()?; // `foo` @@ -2398,6 +2401,8 @@ impl<'a> Parser<'a> { // inside `parse_fn_body()`. let fn_params_end = self.prev_token.span.shrink_to_hi(); + let contract = self.parse_contract()?; + generics.where_clause = self.parse_where_clause()?; // `where T: Ord` // `fn_params_end` is needed only when it's followed by a where clause. @@ -2409,7 +2414,7 @@ impl<'a> Parser<'a> { let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?; let fn_sig_span = sig_lo.to(sig_hi); - Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body)) + Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, contract, body)) } /// Provide diagnostics when function body is not found diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index 73f3ac001c8ae..50f03e72f82dc 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -108,6 +108,8 @@ pub enum TokenType { KwRef, KwReturn, KwReuse, + KwRustcContractEnsures, + KwRustcContractRequires, KwSafe, KwSelfUpper, KwStatic, @@ -242,6 +244,8 @@ impl TokenType { KwRef, KwReturn, KwReuse, + KwRustcContractEnsures, + KwRustcContractRequires, KwSafe, KwSelfUpper, KwStatic, @@ -314,6 +318,8 @@ impl TokenType { TokenType::KwRef => Some(kw::Ref), TokenType::KwReturn => Some(kw::Return), TokenType::KwReuse => Some(kw::Reuse), + TokenType::KwRustcContractEnsures => Some(kw::RustcContractEnsures), + TokenType::KwRustcContractRequires => Some(kw::RustcContractRequires), TokenType::KwSafe => Some(kw::Safe), TokenType::KwSelfUpper => Some(kw::SelfUpper), TokenType::KwStatic => Some(kw::Static), @@ -544,6 +550,8 @@ macro_rules! exp { (Ref) => { exp!(@kw, Ref, KwRef) }; (Return) => { exp!(@kw, Return, KwReturn) }; (Reuse) => { exp!(@kw, Reuse, KwReuse) }; + (RustcContractEnsures) => { exp!(@kw, RustcContractEnsures, KwRustcContractEnsures) }; + (RustcContractRequires) => { exp!(@kw, RustcContractRequires, KwRustcContractRequires) }; (Safe) => { exp!(@kw, Safe, KwSafe) }; (SelfUpper) => { exp!(@kw, SelfUpper, KwSelfUpper) }; (Static) => { exp!(@kw, Static, KwStatic) }; diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 16c0a345f8791..db607dbb41963 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -174,10 +174,13 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { _ctxt, _ident, _vis, - Fn { sig: FnSig { header, decl, span: _ }, generics, body, .. }, + Fn { sig: FnSig { header, decl, span: _ }, generics, contract, body, .. }, ) if let Some(coroutine_kind) = header.coroutine_kind => { self.visit_fn_header(header); self.visit_generics(generics); + if let Some(contract) = contract { + self.visit_contract(contract); + } // For async functions, we need to create their inner defs inside of a // closure to match their desugared representation. Besides that, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4842cbd556c37..e37e7e98ee7aa 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1019,7 +1019,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r // Create a label rib for the function. this.with_label_rib(RibKind::FnOrCoroutine, |this| { match fn_kind { - FnKind::Fn(_, _, _, Fn { sig, generics, body, .. }) => { + FnKind::Fn(_, _, _, Fn { sig, generics, contract, body, .. }) => { this.visit_generics(generics); let declaration = &sig.decl; @@ -1046,6 +1046,10 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r }, ); + if let Some(contract) = contract { + this.visit_contract(contract); + } + if let Some(body) = body { // Ignore errors in function bodies if this is rustdoc // Be sure not to set this until the function signature has been resolved. diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index a5826137181db..dbbbb5077cb5c 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1163,6 +1163,8 @@ pub enum DesugaringKind { WhileLoop, /// `async Fn()` bound modifier BoundModifier, + /// Calls to contract checks (`#[requires]` to precond, `#[ensures]` to postcond) + Contract, } impl DesugaringKind { @@ -1179,6 +1181,7 @@ impl DesugaringKind { DesugaringKind::ForLoop => "`for` loop", DesugaringKind::WhileLoop => "`while` loop", DesugaringKind::BoundModifier => "trait bound modifier", + DesugaringKind::Contract => "contract check", } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index df424b3312a0f..1c15ca7d11aa8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -118,6 +118,8 @@ symbols! { MacroRules: "macro_rules", Raw: "raw", Reuse: "reuse", + RustcContractEnsures: "rustc_contract_ensures", + RustcContractRequires: "rustc_contract_requires", Safe: "safe", Union: "union", Yeet: "yeet", diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index fd283fe92fa36..a31cca5d425c9 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -113,8 +113,8 @@ #![feature(bigint_helper_methods)] #![feature(bstr)] #![feature(bstr_internals)] -#![feature(const_carrying_mul_add)] #![feature(closure_track_caller)] +#![feature(const_carrying_mul_add)] #![feature(const_eval_select)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 2eb09bac8d881..798f4575c2e10 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -362,18 +362,21 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { defaultness: ld, sig: lf, generics: lg, + contract: lc, body: lb, }), Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, + contract: rc, body: rb, }), ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) + && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) }, (Mod(lu, lmk), Mod(ru, rmk)) => { @@ -497,18 +500,21 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { defaultness: ld, sig: lf, generics: lg, + contract: lc, body: lb, }), Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, + contract: rc, body: rb, }), ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) + && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) }, ( @@ -559,18 +565,21 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { defaultness: ld, sig: lf, generics: lg, + contract: lc, body: lb, }), Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, + contract: rc, body: rb, }), ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) + && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) }, ( @@ -653,6 +662,17 @@ pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool { && eq_ext(&l.ext, &r.ext) } +pub fn eq_opt_fn_contract(l: &Option>, r: &Option>) -> bool { + match (l, r) { + (Some(l), Some(r)) => { + eq_expr_opt(l.requires.as_ref(), r.requires.as_ref()) + && eq_expr_opt(l.ensures.as_ref(), r.ensures.as_ref()) + } + (None, None) => true, + (Some(_), None) | (None, Some(_)) => false, + } +} + pub fn eq_generics(l: &Generics, r: &Generics) -> bool { over(&l.params, &r.params, eq_generic_param) && over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| { diff --git a/tests/ui/contracts/contract-ast-extensions-nest.rs b/tests/ui/contracts/contract-ast-extensions-nest.rs new file mode 100644 index 0000000000000..ed137c4a98671 --- /dev/null +++ b/tests/ui/contracts/contract-ast-extensions-nest.rs @@ -0,0 +1,44 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +fn nest(x: Baz) -> i32 + rustc_contract_requires(|| x.baz > 0) + rustc_contract_ensures(|ret| *ret > 100) +{ + loop { + return x.baz + 50; + } +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(nest(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + nest(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + nest(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/contract-ast-extensions-tail.rs b/tests/ui/contracts/contract-ast-extensions-tail.rs new file mode 100644 index 0000000000000..b501c3faaed69 --- /dev/null +++ b/tests/ui/contracts/contract-ast-extensions-tail.rs @@ -0,0 +1,42 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +fn tail(x: Baz) -> i32 + rustc_contract_requires(|| x.baz > 0) + rustc_contract_ensures(|ret| *ret > 100) +{ + x.baz + 50 +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(tail(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + tail(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + tail(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs new file mode 100644 index 0000000000000..069f26e6796be --- /dev/null +++ b/tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs @@ -0,0 +1,15 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(rustc_contracts)] + +fn outer() -> i32 + rustc_contract_ensures(|ret| *ret > 0) +{ + let inner_closure = || -> i32 { 0 }; + inner_closure(); + 10 +} + +fn main() { + outer(); +} diff --git a/tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs new file mode 100644 index 0000000000000..fbee8277a968e --- /dev/null +++ b/tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs @@ -0,0 +1,17 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(rustc_contracts)] + +struct Outer { outer: std::cell::Cell } + +fn outer(x: Outer) + rustc_contract_requires(|| x.outer.get() > 0) +{ + let inner_closure = || { }; + x.outer.set(0); + inner_closure(); +} + +fn main() { + outer(Outer { outer: 1.into() }); +} From 4636dd9347573e81056f88189ac52ee1e8ccc2f7 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 3 Feb 2025 13:01:13 -0800 Subject: [PATCH 17/28] Add tests for nested macro_rules edition behavior This adds tests to check the behavior of how nested macro_rules definitions work across edition boundaries. This covers a change in behavior due to https://github.com/rust-lang/rust/pull/133274. See https://github.com/rust-lang/rust/issues/135669 --- .../auxiliary/nested_macro_rules_dep_2021.rs | 23 +++++++++++ .../auxiliary/nested_macro_rules_dep_2024.rs | 23 +++++++++++ .../nested-macro-rules-edition.e2021.stderr | 27 +++++++++++++ .../ui/editions/nested-macro-rules-edition.rs | 39 +++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs create mode 100644 tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs create mode 100644 tests/ui/editions/nested-macro-rules-edition.e2021.stderr create mode 100644 tests/ui/editions/nested-macro-rules-edition.rs diff --git a/tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs new file mode 100644 index 0000000000000..36d3e712dc0e9 --- /dev/null +++ b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs @@ -0,0 +1,23 @@ +//@ edition: 2021 + +#[macro_export] +macro_rules! make_macro_with_input { + ($i:ident) => { + macro_rules! macro_inner_input { + () => { + pub fn $i() {} + }; + } + }; +} + +#[macro_export] +macro_rules! make_macro { + () => { + macro_rules! macro_inner { + () => { + pub fn gen() {} + }; + } + }; +} diff --git a/tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs new file mode 100644 index 0000000000000..4012398fe6622 --- /dev/null +++ b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs @@ -0,0 +1,23 @@ +//@ edition: 2024 + +#[macro_export] +macro_rules! make_macro_with_input { + ($i:ident) => { + macro_rules! macro_inner_input { + () => { + pub fn $i() {} + }; + } + }; +} + +#[macro_export] +macro_rules! make_macro { + () => { + macro_rules! macro_inner { + () => { + pub fn gen() {} + }; + } + }; +} diff --git a/tests/ui/editions/nested-macro-rules-edition.e2021.stderr b/tests/ui/editions/nested-macro-rules-edition.e2021.stderr new file mode 100644 index 0000000000000..eac80262364ba --- /dev/null +++ b/tests/ui/editions/nested-macro-rules-edition.e2021.stderr @@ -0,0 +1,27 @@ +error: expected identifier, found reserved keyword `gen` + --> $DIR/nested-macro-rules-edition.rs:30:5 + | +LL | macro_inner_input!{} + | ^^^^^^^^^^^^^^^^^^^^ expected identifier, found reserved keyword + | + = note: this error originates in the macro `macro_inner_input` (in Nightly builds, run with -Z macro-backtrace for more info) +help: escape `gen` to use it as an identifier + | +LL | nested_macro_rules_dep::make_macro_with_input!{r#gen} + | ++ + +error: expected identifier, found reserved keyword `gen` + --> $DIR/nested-macro-rules-edition.rs:35:5 + | +LL | macro_inner!{} + | ^^^^^^^^^^^^^^ expected identifier, found reserved keyword + | + = note: this error originates in the macro `macro_inner` (in Nightly builds, run with -Z macro-backtrace for more info) +help: escape `gen` to use it as an identifier + --> $DIR/auxiliary/nested_macro_rules_dep_2024.rs:19:24 + | +LL | pub fn r#gen() {} + | ++ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/editions/nested-macro-rules-edition.rs b/tests/ui/editions/nested-macro-rules-edition.rs new file mode 100644 index 0000000000000..28d568f77501b --- /dev/null +++ b/tests/ui/editions/nested-macro-rules-edition.rs @@ -0,0 +1,39 @@ +// This checks the behavior of how nested macro_rules definitions are handled +// with regards to edition spans. Prior to https://github.com/rust-lang/rust/pull/133274, +// the compiler would compile the inner macro with the edition of the local crate. +// Afterwards, it uses the edition where the macro was *defined*. +// +// Unfortunately macro_rules compiler discards the edition of any *input* that +// was used to generate the macro. This is possibly not the behavior that we +// want. If we want to keep with the philosophy that code should follow the +// edition rules of the crate where it is written, then presumably we would +// want the input tokens to retain the edition of where they were written. +// +// See https://github.com/rust-lang/rust/issues/135669 for more. +// +// This has two revisions, one where local=2021 and the dep=2024. The other +// revision is vice-versa. + +//@ revisions: e2021 e2024 +//@[e2021] edition:2021 +//@[e2024] edition:2024 +//@[e2021] aux-crate: nested_macro_rules_dep=nested_macro_rules_dep_2024.rs +//@[e2024] aux-crate: nested_macro_rules_dep=nested_macro_rules_dep_2021.rs +//@[e2024] check-pass + +mod with_input { + // If we change the macro_rules input behavior, then this should pass when + // local edition is 2021 because `gen` is written in a context with 2021 + // behavior. For local edition 2024, the reverse would be true and this + // should fail. + nested_macro_rules_dep::make_macro_with_input!{gen} + macro_inner_input!{} + //[e2021]~^ ERROR found reserved keyword +} +mod no_input { + nested_macro_rules_dep::make_macro!{} + macro_inner!{} + //[e2021]~^ ERROR found reserved keyword +} + +fn main() {} From ae7eff0be5c4abae63c06851af14cfdf7fec3981 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 3 Dec 2024 02:52:29 +0000 Subject: [PATCH 18/28] Desugars contract into the internal AST extensions Check ensures on early return due to Try / Yeet Expand these two expressions to include a call to contract checking --- compiler/rustc_ast_lowering/src/expr.rs | 48 +++-- compiler/rustc_ast_lowering/src/item.rs | 115 +++++------- .../rustc_builtin_macros/src/contracts.rs | 172 ++++++++++++++++++ compiler/rustc_builtin_macros/src/lib.rs | 5 + compiler/rustc_span/src/symbol.rs | 2 + library/core/src/contracts.rs | 5 + library/core/src/macros/mod.rs | 26 +++ .../ui/contracts/contract-attributes-nest.rs | 44 +++++ .../ui/contracts/contract-attributes-tail.rs | 42 +++++ .../contracts-ensures-early-fn-exit.rs | 48 +++++ ...s-ensures-is-not-inherited-when-nesting.rs | 14 ++ ...-requires-is-not-inherited-when-nesting.rs | 16 ++ 12 files changed, 453 insertions(+), 84 deletions(-) create mode 100644 compiler/rustc_builtin_macros/src/contracts.rs create mode 100644 tests/ui/contracts/contract-attributes-nest.rs create mode 100644 tests/ui/contracts/contract-attributes-tail.rs create mode 100644 tests/ui/contracts/contracts-ensures-early-fn-exit.rs create mode 100644 tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs create mode 100644 tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 19433047595a0..8125da361b814 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -314,21 +314,8 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Continue(self.lower_jump_destination(e.id, *opt_label)) } ExprKind::Ret(e) => { - let mut e = e.as_ref().map(|x| self.lower_expr(x)); - if let Some(Some((span, fresh_ident))) = self - .contract - .as_ref() - .map(|c| c.ensures.as_ref().map(|e| (e.expr.span, e.fresh_ident))) - { - let checker_fn = self.expr_ident(span, fresh_ident.0, fresh_ident.2); - let args = if let Some(e) = e { - std::slice::from_ref(e) - } else { - std::slice::from_ref(self.expr_unit(span)) - }; - e = Some(self.expr_call(span, checker_fn, args)); - } - hir::ExprKind::Ret(e) + let expr = e.as_ref().map(|x| self.lower_expr(x)); + self.checked_return(expr) } ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()), ExprKind::Become(sub_expr) => { @@ -395,6 +382,32 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } + /// Create an `ExprKind::Ret` that is preceded by a call to check contract ensures clause. + fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> { + let checked_ret = if let Some(Some((span, fresh_ident))) = + self.contract.as_ref().map(|c| c.ensures.as_ref().map(|e| (e.expr.span, e.fresh_ident))) + { + let expr = opt_expr.unwrap_or_else(|| self.expr_unit(span)); + Some(self.inject_ensures_check(expr, span, fresh_ident.0, fresh_ident.2)) + } else { + opt_expr + }; + hir::ExprKind::Ret(checked_ret) + } + + /// Wraps an expression with a call to the ensures check before it gets returned. + pub(crate) fn inject_ensures_check( + &mut self, + expr: &'hir hir::Expr<'hir>, + span: Span, + check_ident: Ident, + check_hir_id: HirId, + ) -> &'hir hir::Expr<'hir> { + let checker_fn = self.expr_ident(span, check_ident, check_hir_id); + let span = self.mark_span_with_reason(DesugaringKind::Contract, span, None); + self.expr_call(span, checker_fn, std::slice::from_ref(expr)) + } + pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock { self.with_new_scopes(c.value.span, |this| { let def_id = this.local_def_id(c.id); @@ -1983,7 +1996,8 @@ impl<'hir> LoweringContext<'_, 'hir> { ), )) } else { - self.arena.alloc(self.expr(try_span, hir::ExprKind::Ret(Some(from_residual_expr)))) + let ret_expr = self.checked_return(Some(from_residual_expr)); + self.arena.alloc(self.expr(try_span, ret_expr)) }; self.lower_attrs(ret_expr.hir_id, &attrs); @@ -2032,7 +2046,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let target_id = Ok(catch_id); hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr)) } else { - hir::ExprKind::Ret(Some(from_yeet_expr)) + self.checked_return(Some(from_yeet_expr)) } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 4679ccdddbbe1..c96110fee619b 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -215,7 +215,7 @@ impl<'hir> LoweringContext<'_, 'hir> { if let Some(contract) = contract { let requires = contract.requires.clone(); let ensures = contract.ensures.clone(); - let ensures = if let Some(ens) = ensures { + let ensures = ensures.map(|ens| { // FIXME: this needs to be a fresh (or illegal) identifier to prevent // accidental capture of a parameter or global variable. let checker_ident: Ident = @@ -226,13 +226,11 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::BindingMode::NONE, ); - Some(crate::FnContractLoweringEnsures { + crate::FnContractLoweringEnsures { expr: ens, fresh_ident: (checker_ident, checker_pat, checker_hir_id), - }) - } else { - None - }; + } + }); // Note: `with_new_scopes` will reinstall the outer // item's contract (if any) after its callback finishes. @@ -1095,73 +1093,56 @@ impl<'hir> LoweringContext<'_, 'hir> { // { body } // ==> - // { rustc_contract_requires(PRECOND); { body } } - let result: hir::Expr<'hir> = if let Some(contract) = opt_contract { - let lit_unit = |this: &mut LoweringContext<'_, 'hir>| { - this.expr(contract.span, hir::ExprKind::Tup(&[])) - }; - - let precond: hir::Stmt<'hir> = if let Some(req) = contract.requires { - let lowered_req = this.lower_expr_mut(&req); - let precond = this.expr_call_lang_item_fn_mut( - req.span, - hir::LangItem::ContractCheckRequires, - &*arena_vec![this; lowered_req], - ); - this.stmt_expr(req.span, precond) - } else { - let u = lit_unit(this); - this.stmt_expr(contract.span, u) - }; + // { contract_requires(PRECOND); { body } } + let Some(contract) = opt_contract else { return (params, result) }; + let result_ref = this.arena.alloc(result); + let lit_unit = |this: &mut LoweringContext<'_, 'hir>| { + this.expr(contract.span, hir::ExprKind::Tup(&[])) + }; - let (postcond_checker, result) = if let Some(ens) = contract.ensures { - let crate::FnContractLoweringEnsures { expr: ens, fresh_ident } = ens; - let lowered_ens: hir::Expr<'hir> = this.lower_expr_mut(&ens); - let postcond_checker = this.expr_call_lang_item_fn( - ens.span, - hir::LangItem::ContractBuildCheckEnsures, - &*arena_vec![this; lowered_ens], - ); - let checker_binding_pat = fresh_ident.1; - ( - this.stmt_let_pat( - None, - ens.span, - Some(postcond_checker), - this.arena.alloc(checker_binding_pat), - hir::LocalSource::Contract, - ), - { - let checker_fn = - this.expr_ident(ens.span, fresh_ident.0, fresh_ident.2); - let span = this.mark_span_with_reason( - DesugaringKind::Contract, - ens.span, - None, - ); - this.expr_call_mut( - span, - checker_fn, - std::slice::from_ref(this.arena.alloc(result)), - ) - }, - ) - } else { - let u = lit_unit(this); - (this.stmt_expr(contract.span, u), result) - }; + let precond: hir::Stmt<'hir> = if let Some(req) = contract.requires { + let lowered_req = this.lower_expr_mut(&req); + let precond = this.expr_call_lang_item_fn_mut( + req.span, + hir::LangItem::ContractCheckRequires, + &*arena_vec![this; lowered_req], + ); + this.stmt_expr(req.span, precond) + } else { + let u = lit_unit(this); + this.stmt_expr(contract.span, u) + }; - let block = this.block_all( - contract.span, - arena_vec![this; precond, postcond_checker], - Some(this.arena.alloc(result)), + let (postcond_checker, result) = if let Some(ens) = contract.ensures { + let crate::FnContractLoweringEnsures { expr: ens, fresh_ident } = ens; + let lowered_ens: hir::Expr<'hir> = this.lower_expr_mut(&ens); + let postcond_checker = this.expr_call_lang_item_fn( + ens.span, + hir::LangItem::ContractBuildCheckEnsures, + &*arena_vec![this; lowered_ens], ); - this.expr_block(block) + let checker_binding_pat = fresh_ident.1; + ( + this.stmt_let_pat( + None, + ens.span, + Some(postcond_checker), + this.arena.alloc(checker_binding_pat), + hir::LocalSource::Contract, + ), + this.inject_ensures_check(result_ref, ens.span, fresh_ident.0, fresh_ident.2), + ) } else { - result + let u = lit_unit(this); + (this.stmt_expr(contract.span, u), &*result_ref) }; - (params, result) + let block = this.block_all( + contract.span, + arena_vec![this; precond, postcond_checker], + Some(result), + ); + (params, this.expr_block(block)) }) } diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs new file mode 100644 index 0000000000000..d269287b555b3 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -0,0 +1,172 @@ +#![allow(unused_imports, unused_variables)] + +use rustc_ast::token; +use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; +use rustc_errors::ErrorGuaranteed; +use rustc_expand::base::{AttrProcMacro, ExtCtxt}; +use rustc_span::Span; +use rustc_span::symbol::{Ident, Symbol, kw, sym}; + +pub(crate) struct ExpandRequires; + +pub(crate) struct ExpandEnsures; + +impl AttrProcMacro for ExpandRequires { + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + annotation: TokenStream, + annotated: TokenStream, + ) -> Result { + expand_requires_tts(ecx, span, annotation, annotated) + } +} + +impl AttrProcMacro for ExpandEnsures { + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + annotation: TokenStream, + annotated: TokenStream, + ) -> Result { + expand_ensures_tts(ecx, span, annotation, annotated) + } +} + +fn expand_injecting_circa_where_clause( + _ecx: &mut ExtCtxt<'_>, + attr_span: Span, + annotated: TokenStream, + inject: impl FnOnce(&mut Vec) -> Result<(), ErrorGuaranteed>, +) -> Result { + let mut new_tts = Vec::with_capacity(annotated.len()); + let mut cursor = annotated.into_trees(); + + // Find the `fn name(x:X,...)` and inject the AST contract forms right after + // the formal parameters (and return type if any). + while let Some(tt) = cursor.next_ref() { + new_tts.push(tt.clone()); + if let TokenTree::Token(tok, _) = tt + && tok.is_ident_named(kw::Fn) + { + break; + } + } + + // Found the `fn` keyword, now find the formal parameters. + // + // FIXME: can this fail if you have parentheticals in a generics list, like `fn foo Y>` ? + while let Some(tt) = cursor.next_ref() { + new_tts.push(tt.clone()); + + if let TokenTree::Delimited(_, _, token::Delimiter::Parenthesis, _) = tt { + break; + } + if let TokenTree::Token(token::Token { kind: token::TokenKind::Semi, .. }, _) = tt { + panic!("contract attribute applied to fn without parameter list."); + } + } + + // There *might* be a return type declaration (and figuring out where that ends would require + // parsing an arbitrary type expression, e.g. `-> Foo` + // + // Instead of trying to figure that out, scan ahead and look for the first occurence of a + // `where`, a `{ ... }`, or a `;`. + // + // FIXME: this might still fall into a trap for something like `-> Ctor`. I + // *think* such cases must be under a Delimited (e.g. `[T; { N }]` or have the braced form + // prefixed by e.g. `const`, so we should still be able to filter them out without having to + // parse the type expression itself. But rather than try to fix things with hacks like that, + // time might be better spent extending the attribute expander to suport tt-annotation atop + // ast-annotated, which would be an elegant way to sidestep all of this. + let mut opt_next_tt = cursor.next_ref(); + while let Some(next_tt) = opt_next_tt { + if let TokenTree::Token(tok, _) = next_tt + && tok.is_ident_named(kw::Where) + { + break; + } + if let TokenTree::Delimited(_, _, token::Delimiter::Brace, _) = next_tt { + break; + } + if let TokenTree::Token(token::Token { kind: token::TokenKind::Semi, .. }, _) = next_tt { + break; + } + + // for anything else, transcribe the tt and keep looking. + new_tts.push(next_tt.clone()); + opt_next_tt = cursor.next_ref(); + continue; + } + + // At this point, we've transcribed everything from the `fn` through the formal parameter list + // and return type declaration, (if any), but `tt` itself has *not* been transcribed. + // + // Now inject the AST contract form. + // + // FIXME: this kind of manual token tree munging does not have significant precedent among + // rustc builtin macros, probably because most builtin macros use direct AST manipulation to + // accomplish similar goals. But since our attributes need to take arbitrary expressions, and + // our attribute infrastructure does not yet support mixing a token-tree annotation with an AST + // annotated, we end up doing token tree manipulation. + inject(&mut new_tts)?; + + // Above we injected the internal AST requires/ensures contruct. Now copy over all the other + // token trees. + if let Some(tt) = opt_next_tt { + new_tts.push(tt.clone()); + } + while let Some(tt) = cursor.next_ref() { + new_tts.push(tt.clone()); + } + + Ok(TokenStream::new(new_tts)) +} + +fn expand_requires_tts( + _ecx: &mut ExtCtxt<'_>, + attr_span: Span, + annotation: TokenStream, + annotated: TokenStream, +) -> Result { + expand_injecting_circa_where_clause(_ecx, attr_span, annotated, |new_tts| { + new_tts.push(TokenTree::Token( + token::Token::from_ast_ident(Ident::new(kw::RustcContractRequires, attr_span)), + Spacing::Joint, + )); + new_tts.push(TokenTree::Token( + token::Token::new(token::TokenKind::OrOr, attr_span), + Spacing::Alone, + )); + new_tts.push(TokenTree::Delimited( + DelimSpan::from_single(attr_span), + DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), + token::Delimiter::Parenthesis, + annotation, + )); + Ok(()) + }) +} + +fn expand_ensures_tts( + _ecx: &mut ExtCtxt<'_>, + attr_span: Span, + annotation: TokenStream, + annotated: TokenStream, +) -> Result { + expand_injecting_circa_where_clause(_ecx, attr_span, annotated, |new_tts| { + new_tts.push(TokenTree::Token( + token::Token::from_ast_ident(Ident::new(kw::RustcContractEnsures, attr_span)), + Spacing::Joint, + )); + new_tts.push(TokenTree::Delimited( + DelimSpan::from_single(attr_span), + DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), + token::Delimiter::Parenthesis, + annotation, + )); + Ok(()) + }) +} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 6987ae95980a6..ca16583a45de7 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -55,6 +55,7 @@ mod trace_macros; pub mod asm; pub mod cmdline_attrs; +pub mod contracts; pub mod proc_macro_harness; pub mod standard_library_imports; pub mod test_harness; @@ -137,4 +138,8 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote); register(sym::quote, SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client }))); + let requires = SyntaxExtensionKind::Attr(Box::new(contracts::ExpandRequires)); + register(sym::contracts_requires, requires); + let ensures = SyntaxExtensionKind::Attr(Box::new(contracts::ExpandEnsures)); + register(sym::contracts_ensures, ensures); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1c15ca7d11aa8..43bc69e6e7cfe 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -682,6 +682,8 @@ symbols! { contract_check_ensures, contract_check_requires, contract_checks, + contracts_ensures, + contracts_requires, convert_identity, copy, copy_closures, diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs index c13565d59d58f..3efd2df0a3832 100644 --- a/library/core/src/contracts.rs +++ b/library/core/src/contracts.rs @@ -1,5 +1,10 @@ //! Unstable module containing the unstable contracts lang items and attribute macros. +#[cfg(not(bootstrap))] +pub use crate::macros::builtin::contracts_ensures as ensures; +#[cfg(not(bootstrap))] +pub use crate::macros::builtin::contracts_requires as requires; + /// Emitted by rustc as a desugaring of `#[requires(PRED)] fn foo(x: X) { ... }` /// into: `fn foo(x: X) { check_requires(|| PRED) ... }` #[cfg(not(bootstrap))] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 3669051ce82d3..1a5d2973dfc3f 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1777,6 +1777,32 @@ pub(crate) mod builtin { /* compiler built-in */ } + /// Attribute macro applied to a function to give it a post-condition. + /// + /// The attribute carries an argument token-tree which is + /// eventually parsed as a unary closure expression that is + /// invoked on a reference to the return value. + #[cfg(not(bootstrap))] + #[unstable(feature = "rustc_contracts", issue = "none")] + #[allow_internal_unstable(core_intrinsics)] + #[rustc_builtin_macro] + pub macro contracts_ensures($item:item) { + /* compiler built-in */ + } + + /// Attribute macro applied to a function to give it a precondition. + /// + /// The attribute carries an argument token-tree which is + /// eventually parsed as an boolean expression with access to the + /// function's formal parameters + #[cfg(not(bootstrap))] + #[unstable(feature = "rustc_contracts", issue = "none")] + #[allow_internal_unstable(core_intrinsics)] + #[rustc_builtin_macro] + pub macro contracts_requires($item:item) { + /* compiler built-in */ + } + /// Attribute macro applied to a function to register it as a handler for allocation failure. /// /// See also [`std::alloc::handle_alloc_error`](../../../std/alloc/fn.handle_alloc_error.html). diff --git a/tests/ui/contracts/contract-attributes-nest.rs b/tests/ui/contracts/contract-attributes-nest.rs new file mode 100644 index 0000000000000..1cda21f10d7ec --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.rs @@ -0,0 +1,44 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures(|ret| *ret > 100)] +fn nest(x: Baz) -> i32 +{ + loop { + return x.baz + 50; + } +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(nest(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + nest(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + nest(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/contract-attributes-tail.rs b/tests/ui/contracts/contract-attributes-tail.rs new file mode 100644 index 0000000000000..26855bfa82ac4 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.rs @@ -0,0 +1,42 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures(|ret| *ret > 100)] +fn tail(x: Baz) -> i32 +{ + x.baz + 50 +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(tail(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + tail(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + tail(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.rs b/tests/ui/contracts/contracts-ensures-early-fn-exit.rs new file mode 100644 index 0000000000000..faf97473a90f2 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.rs @@ -0,0 +1,48 @@ +//@ revisions: unchk_pass chk_pass chk_fail_try chk_fail_ret chk_fail_yeet +// +//@ [unchk_pass] run-pass +//@ [chk_pass] run-pass +//@ [chk_fail_try] run-fail +//@ [chk_fail_ret] run-fail +//@ [chk_fail_yeet] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_try] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_ret] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_yeet] compile-flags: -Zcontract-checks=yes +//! This test ensures that ensures clauses are checked for different return points of a function. + +#![feature(rustc_contracts)] +#![feature(yeet_expr)] + +/// This ensures will fail in different return points depending on the input. +#[core::contracts::ensures(|ret: &Option| ret.is_some())] +fn try_sum(x: u32, y: u32, z: u32) -> Option { + // Use Yeet to return early. + if x == u32::MAX && (y > 0 || z > 0) { do yeet } + + // Use `?` to early return. + let partial = x.checked_add(y)?; + + // Explicitly use `return` clause. + if u32::MAX - partial < z { + return None; + } + + Some(partial + z) +} + +fn main() { + // This should always succeed + assert_eq!(try_sum(0, 1, 2), Some(3)); + + #[cfg(any(unchk_pass, chk_fail_yeet))] + assert_eq!(try_sum(u32::MAX, 1, 1), None); + + #[cfg(any(unchk_pass, chk_fail_try))] + assert_eq!(try_sum(u32::MAX - 10, 12, 0), None); + + #[cfg(any(unchk_pass, chk_fail_ret))] + assert_eq!(try_sum(u32::MAX - 10, 2, 100), None); +} diff --git a/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs new file mode 100644 index 0000000000000..9872fdb1a1852 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs @@ -0,0 +1,14 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(rustc_contracts)] + +#[core::contracts::ensures(|ret| *ret > 0)] +fn outer() -> i32 { + let inner_closure = || -> i32 { 0 }; + inner_closure(); + 10 +} + +fn main() { + outer(); +} diff --git a/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs new file mode 100644 index 0000000000000..75124259b0d72 --- /dev/null +++ b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs @@ -0,0 +1,16 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(rustc_contracts)] + +struct Outer { outer: std::cell::Cell } + +#[core::contracts::requires(x.outer.get() > 0)] +fn outer(x: Outer) { + let inner_closure = || { }; + x.outer.set(0); + inner_closure(); +} + +fn main() { + outer(Outer { outer: 1.into() }); +} From b279ff9dcfadcdb6976097d58044d151af81cf51 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 3 Dec 2024 14:29:27 +0000 Subject: [PATCH 19/28] demonstrate how to capture state at precondition time and feed into postcondition predicate. --- .../contract-captures-via-closure-copy.rs | 25 +++++++++++++++++ .../contract-captures-via-closure-noncopy.rs | 22 +++++++++++++++ ...ntract-captures-via-closure-noncopy.stderr | 27 +++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 tests/ui/contracts/contract-captures-via-closure-copy.rs create mode 100644 tests/ui/contracts/contract-captures-via-closure-noncopy.rs create mode 100644 tests/ui/contracts/contract-captures-via-closure-noncopy.stderr diff --git a/tests/ui/contracts/contract-captures-via-closure-copy.rs b/tests/ui/contracts/contract-captures-via-closure-copy.rs new file mode 100644 index 0000000000000..742895ab0ad80 --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-copy.rs @@ -0,0 +1,25 @@ +//@ run-fail +//@ compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +struct Baz { + baz: i32 +} + +#[track_caller] +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures({let old = x.baz; move |ret:&Baz| ret.baz == old*2 })] +// Relevant thing is this: ^^^^^^^^^^^^^^^ +// because we are capturing state that is Copy +fn doubler(x: Baz) -> Baz { + Baz { baz: x.baz + 10 } +} + +fn main() { + assert_eq!(doubler(Baz { baz: 10 }).baz, 20); + assert_eq!(doubler(Baz { baz: 100 }).baz, 200); + // This is a *run-fail* test because it is still exercising the + // contract machinery, specifically because this second invocation + // of `doubler` shows how the code does not meet its contract. +} diff --git a/tests/ui/contracts/contract-captures-via-closure-noncopy.rs b/tests/ui/contracts/contract-captures-via-closure-noncopy.rs new file mode 100644 index 0000000000000..8d7f2fd200e2c --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-noncopy.rs @@ -0,0 +1,22 @@ +//@ compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +struct Baz { + baz: i32 +} + +#[track_caller] +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] +// Relevant thing is this: ^^^^^^^^^^^ +// because we are capturing state that is non-Copy. +//~^^^ ERROR trait bound `Baz: std::marker::Copy` is not satisfied +fn doubler(x: Baz) -> Baz { + Baz { baz: x.baz + 10 } +} + +fn main() { + assert_eq!(doubler(Baz { baz: 10 }).baz, 20); + assert_eq!(doubler(Baz { baz: 100 }).baz, 200); +} diff --git a/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr new file mode 100644 index 0000000000000..b53809827f987 --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `Baz: std::marker::Copy` is not satisfied in `{closure@$DIR/contract-captures-via-closure-noncopy.rs:11:42: 11:57}` + --> $DIR/contract-captures-via-closure-noncopy.rs:11:1 + | +LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------------------------------^^^^ + | | | + | | within this `{closure@$DIR/contract-captures-via-closure-noncopy.rs:11:42: 11:57}` + | | this tail expression is of type `{closure@contract-captures-via-closure-noncopy.rs:11:42}` + | unsatisfied trait bound + | + = help: within `{closure@$DIR/contract-captures-via-closure-noncopy.rs:11:42: 11:57}`, the trait `std::marker::Copy` is not implemented for `Baz` +note: required because it's used within this closure + --> $DIR/contract-captures-via-closure-noncopy.rs:11:42 + | +LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] + | ^^^^^^^^^^^^^^^ +note: required by a bound in `build_check_ensures` + --> $SRC_DIR/core/src/contracts.rs:LL:COL +help: consider annotating `Baz` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | struct Baz { + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From 6a6c6b891bb0350b3f16abd3e84ff12dbd1b4c5b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 3 Dec 2024 16:13:00 +0000 Subject: [PATCH 20/28] Separate contract feature gates for the internal machinery The extended syntax for function signature that includes contract clauses should never be user exposed versus the interface we want to ship externally eventually. --- compiler/rustc_ast_passes/src/feature_gate.rs | 2 + .../rustc_builtin_macros/src/contracts.rs | 20 +++-- compiler/rustc_feature/src/unstable.rs | 4 + compiler/rustc_parse/src/parser/generics.rs | 20 ++++- compiler/rustc_session/src/parse.rs | 5 ++ compiler/rustc_span/src/symbol.rs | 2 + library/core/src/contracts.rs | 4 +- library/core/src/intrinsics/mod.rs | 8 +- library/core/src/lib.rs | 2 +- library/core/src/macros/mod.rs | 8 +- .../contract-ast-extensions-nest.rs | 2 +- .../contract-ast-extensions-tail.rs | 2 +- .../contract-intrinsics.rs | 2 +- .../contract-lang-items.rs | 3 +- ...g-ensures-is-not-inherited-when-nesting.rs | 2 +- ...-requires-is-not-inherited-when-nesting.rs | 2 +- .../internal-feature-gating.rs | 23 ++++++ .../internal-feature-gating.stderr | 73 +++++++++++++++++++ .../feature-gate-rustc-contracts.rs | 11 +++ .../feature-gate-rustc-contracts.stderr | 43 +++++++++++ 20 files changed, 210 insertions(+), 28 deletions(-) rename tests/ui/contracts/{ => internal_machinery}/contract-ast-extensions-nest.rs (96%) rename tests/ui/contracts/{ => internal_machinery}/contract-ast-extensions-tail.rs (96%) rename tests/ui/contracts/{ => internal_machinery}/contract-intrinsics.rs (91%) rename tests/ui/contracts/{ => internal_machinery}/contract-lang-items.rs (90%) rename tests/ui/contracts/{ => internal_machinery}/contracts-lowering-ensures-is-not-inherited-when-nesting.rs (84%) rename tests/ui/contracts/{ => internal_machinery}/contracts-lowering-requires-is-not-inherited-when-nesting.rs (88%) create mode 100644 tests/ui/contracts/internal_machinery/internal-feature-gating.rs create mode 100644 tests/ui/contracts/internal_machinery/internal-feature-gating.stderr create mode 100644 tests/ui/feature-gates/feature-gate-rustc-contracts.rs create mode 100644 tests/ui/feature-gates/feature-gate-rustc-contracts.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 80b99f9448567..a3af942a10f17 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -548,6 +548,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); gate_all!(unsafe_fields, "`unsafe` fields are experimental"); gate_all!(unsafe_binders, "unsafe binder types are experimental"); + gate_all!(rustc_contracts, "contracts are experimental"); + gate_all!(rustc_contracts_internals, "contract internal machinery is for internal use only"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs index d269287b555b3..be7f276cdaa93 100644 --- a/compiler/rustc_builtin_macros/src/contracts.rs +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -36,17 +36,17 @@ impl AttrProcMacro for ExpandEnsures { } fn expand_injecting_circa_where_clause( - _ecx: &mut ExtCtxt<'_>, + ecx: &mut ExtCtxt<'_>, attr_span: Span, annotated: TokenStream, inject: impl FnOnce(&mut Vec) -> Result<(), ErrorGuaranteed>, ) -> Result { let mut new_tts = Vec::with_capacity(annotated.len()); - let mut cursor = annotated.into_trees(); + let mut cursor = annotated.iter(); // Find the `fn name(x:X,...)` and inject the AST contract forms right after // the formal parameters (and return type if any). - while let Some(tt) = cursor.next_ref() { + while let Some(tt) = cursor.next() { new_tts.push(tt.clone()); if let TokenTree::Token(tok, _) = tt && tok.is_ident_named(kw::Fn) @@ -58,7 +58,7 @@ fn expand_injecting_circa_where_clause( // Found the `fn` keyword, now find the formal parameters. // // FIXME: can this fail if you have parentheticals in a generics list, like `fn foo Y>` ? - while let Some(tt) = cursor.next_ref() { + while let Some(tt) = cursor.next() { new_tts.push(tt.clone()); if let TokenTree::Delimited(_, _, token::Delimiter::Parenthesis, _) = tt { @@ -81,7 +81,7 @@ fn expand_injecting_circa_where_clause( // parse the type expression itself. But rather than try to fix things with hacks like that, // time might be better spent extending the attribute expander to suport tt-annotation atop // ast-annotated, which would be an elegant way to sidestep all of this. - let mut opt_next_tt = cursor.next_ref(); + let mut opt_next_tt = cursor.next(); while let Some(next_tt) = opt_next_tt { if let TokenTree::Token(tok, _) = next_tt && tok.is_ident_named(kw::Where) @@ -97,8 +97,7 @@ fn expand_injecting_circa_where_clause( // for anything else, transcribe the tt and keep looking. new_tts.push(next_tt.clone()); - opt_next_tt = cursor.next_ref(); - continue; + opt_next_tt = cursor.next(); } // At this point, we've transcribed everything from the `fn` through the formal parameter list @@ -118,10 +117,15 @@ fn expand_injecting_circa_where_clause( if let Some(tt) = opt_next_tt { new_tts.push(tt.clone()); } - while let Some(tt) = cursor.next_ref() { + while let Some(tt) = cursor.next() { new_tts.push(tt.clone()); } + // Record the span as a contract attribute expansion. + // This is used later to stop users from using the extended syntax directly + // which is gated via `rustc_contracts_internals`. + ecx.psess().contract_attribute_spans.push(attr_span); + Ok(TokenStream::new(new_tts)) } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 08a5e22db3a54..57bcd8c5eca22 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -608,6 +608,10 @@ declare_features! ( (unstable, return_type_notation, "1.70.0", Some(109417)), /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), + /// Allows use of contracts attributes. + (unstable, rustc_contracts, "CURRENT_RUSTC_VERSION", Some(133866)), + /// Allows access to internal machinery used to implement contracts. + (unstable, rustc_contracts_internals, "CURRENT_RUSTC_VERSION", Some(133866)), /// Allows use of x86 SHA512, SM3 and SM4 target-features and intrinsics (unstable, sha512_sm_x86, "1.82.0", Some(126624)), /// Allows the use of SIMD types in functions declared in `extern` blocks. diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index d5ac8d1588d7a..14b949dbc3d57 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -4,7 +4,7 @@ use rustc_ast::{ WhereClause, token, }; use rustc_errors::{Applicability, PResult}; -use rustc_span::{Ident, Span, kw}; +use rustc_span::{Ident, Span, kw, sym}; use thin_vec::ThinVec; use super::{ForceCollect, Parser, Trailing, UsePreAttrPos}; @@ -302,13 +302,27 @@ impl<'a> Parser<'a> { pub(super) fn parse_contract( &mut self, ) -> PResult<'a, Option>> { + let gate = |span| { + if self.psess.contract_attribute_spans.contains(span) { + // span was generated via a builtin contracts attribute, so gate as end-user visible + self.psess.gated_spans.gate(sym::rustc_contracts, span); + } else { + // span was not generated via a builtin contracts attribute, so gate as internal machinery + self.psess.gated_spans.gate(sym::rustc_contracts_internals, span); + } + }; + let requires = if self.eat_keyword_noexpect(exp!(RustcContractRequires).kw) { - Some(self.parse_expr()?) + let precond = self.parse_expr()?; + gate(precond.span); + Some(precond) } else { None }; let ensures = if self.eat_keyword_noexpect(exp!(RustcContractEnsures).kw) { - Some(self.parse_expr()?) + let postcond = self.parse_expr()?; + gate(postcond.span); + Some(postcond) } else { None }; diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 81ae06602cdb9..abfd3efc6117c 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -207,6 +207,10 @@ pub struct ParseSess { pub config: Cfg, pub check_config: CheckCfg, pub edition: Edition, + /// Places where contract attributes were expanded into unstable AST forms. + /// This is used to allowlist those spans (so that we only check them against the feature + /// gate for the externally visible interface, and not internal implmentation machinery). + pub contract_attribute_spans: AppendOnlyVec, /// Places where raw identifiers were used. This is used to avoid complaining about idents /// clashing with keywords in new editions. pub raw_identifier_spans: AppendOnlyVec, @@ -255,6 +259,7 @@ impl ParseSess { config: Cfg::default(), check_config: CheckCfg::default(), edition: ExpnId::root().expn_data().edition, + contract_attribute_spans: Default::default(), raw_identifier_spans: Default::default(), bad_unicode_identifiers: Lock::new(Default::default()), source_map, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 43bc69e6e7cfe..ea2ce5475c264 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1716,6 +1716,8 @@ symbols! { rustc_const_stable, rustc_const_stable_indirect, rustc_const_unstable, + rustc_contracts, + rustc_contracts_internals, rustc_conversion_suggestion, rustc_deallocator, rustc_def_path, diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs index 3efd2df0a3832..b155dbc213ee8 100644 --- a/library/core/src/contracts.rs +++ b/library/core/src/contracts.rs @@ -8,7 +8,7 @@ pub use crate::macros::builtin::contracts_requires as requires; /// Emitted by rustc as a desugaring of `#[requires(PRED)] fn foo(x: X) { ... }` /// into: `fn foo(x: X) { check_requires(|| PRED) ... }` #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] #[lang = "contract_check_requires"] #[track_caller] pub fn check_requires bool>(c: C) { @@ -21,7 +21,7 @@ pub fn check_requires bool>(c: C) { /// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }` /// (including the implicit return of the tail expression, if any). #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] #[lang = "contract_build_check_ensures"] #[track_caller] pub fn build_check_ensures(c: C) -> impl (FnOnce(Ret) -> Ret) + Copy diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index beea0996775a4..a7f0f09f0c6b3 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -4051,8 +4051,8 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) /// 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 */)] +#[rustc_const_unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] +#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] #[inline(always)] #[rustc_intrinsic] pub const fn contract_checks() -> bool { @@ -4063,14 +4063,14 @@ pub const fn contract_checks() -> bool { } #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] #[rustc_intrinsic] pub fn contract_check_requires bool>(c: C) -> bool { c() } #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* 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) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index a31cca5d425c9..6a0051244f062 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -248,7 +248,7 @@ pub mod autodiff { } #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts", issue = "none")] +#[unstable(feature = "rustc_contracts", issue = "133866")] pub mod contracts; #[unstable(feature = "cfg_match", issue = "115585")] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 1a5d2973dfc3f..cb37530e90db8 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1783,8 +1783,8 @@ pub(crate) mod builtin { /// eventually parsed as a unary closure expression that is /// invoked on a reference to the return value. #[cfg(not(bootstrap))] - #[unstable(feature = "rustc_contracts", issue = "none")] - #[allow_internal_unstable(core_intrinsics)] + #[unstable(feature = "rustc_contracts", issue = "133866")] + #[allow_internal_unstable(rustc_contracts_internals)] #[rustc_builtin_macro] pub macro contracts_ensures($item:item) { /* compiler built-in */ @@ -1796,8 +1796,8 @@ pub(crate) mod builtin { /// eventually parsed as an boolean expression with access to the /// function's formal parameters #[cfg(not(bootstrap))] - #[unstable(feature = "rustc_contracts", issue = "none")] - #[allow_internal_unstable(core_intrinsics)] + #[unstable(feature = "rustc_contracts", issue = "133866")] + #[allow_internal_unstable(rustc_contracts_internals)] #[rustc_builtin_macro] pub macro contracts_requires($item:item) { /* compiler built-in */ diff --git a/tests/ui/contracts/contract-ast-extensions-nest.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs similarity index 96% rename from tests/ui/contracts/contract-ast-extensions-nest.rs rename to tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs index ed137c4a98671..d95ccd4f7b9b6 100644 --- a/tests/ui/contracts/contract-ast-extensions-nest.rs +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs @@ -16,7 +16,7 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(rustc_contracts_internals)] fn nest(x: Baz) -> i32 rustc_contract_requires(|| x.baz > 0) diff --git a/tests/ui/contracts/contract-ast-extensions-tail.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs similarity index 96% rename from tests/ui/contracts/contract-ast-extensions-tail.rs rename to tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs index b501c3faaed69..636a595e06adb 100644 --- a/tests/ui/contracts/contract-ast-extensions-tail.rs +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs @@ -16,7 +16,7 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(rustc_contracts_internals)] fn tail(x: Baz) -> i32 rustc_contract_requires(|| x.baz > 0) diff --git a/tests/ui/contracts/contract-intrinsics.rs b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs similarity index 91% rename from tests/ui/contracts/contract-intrinsics.rs rename to tests/ui/contracts/internal_machinery/contract-intrinsics.rs index 6e3565baf7ac5..2e1be01e7cabc 100644 --- a/tests/ui/contracts/contract-intrinsics.rs +++ b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs @@ -2,7 +2,7 @@ //@ revisions: yes no none //@ [yes] compile-flags: -Zcontract-checks=yes //@ [no] compile-flags: -Zcontract-checks=no -#![feature(cfg_contract_checks, rustc_contracts, core_intrinsics)] +#![feature(cfg_contract_checks, rustc_contracts_internals, core_intrinsics)] fn main() { #[cfg(none)] // default: disabled diff --git a/tests/ui/contracts/contract-lang-items.rs b/tests/ui/contracts/internal_machinery/contract-lang-items.rs similarity index 90% rename from tests/ui/contracts/contract-lang-items.rs rename to tests/ui/contracts/internal_machinery/contract-lang-items.rs index 1dbf71722fd70..5af467485b10e 100644 --- a/tests/ui/contracts/contract-lang-items.rs +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.rs @@ -16,7 +16,8 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(rustc_contracts)] // to access core::contracts +#![feature(rustc_contracts_internals)] // to access check_requires lang item fn foo(x: Baz) -> i32 { core::contracts::check_requires(|| x.baz > 0); diff --git a/tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs similarity index 84% rename from tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs rename to tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs index 069f26e6796be..0b0151c6df742 100644 --- a/tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs @@ -1,6 +1,6 @@ //@ run-pass //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(rustc_contracts_internals)] fn outer() -> i32 rustc_contract_ensures(|ret| *ret > 0) diff --git a/tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs similarity index 88% rename from tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs rename to tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs index fbee8277a968e..79c50a18f7056 100644 --- a/tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs @@ -1,6 +1,6 @@ //@ run-pass //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(rustc_contracts_internals)] struct Outer { outer: std::cell::Cell } diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs new file mode 100644 index 0000000000000..7b5f17679421f --- /dev/null +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs @@ -0,0 +1,23 @@ +// gate-test-rustc_contracts_internals + +fn main() { + // intrinsics are guarded by rustc_contracts_internals feature gate. + core::intrinsics::contract_checks(); + //~^ ERROR use of unstable library feature `rustc_contracts_internals` + core::intrinsics::contract_check_requires(|| true); + //~^ ERROR use of unstable library feature `rustc_contracts_internals` + core::intrinsics::contract_check_ensures(&1, |_|true); + //~^ ERROR use of unstable library feature `rustc_contracts_internals` + + // lang items are guarded by rustc_contracts_internals feature gate. + core::contracts::check_requires(|| true); + //~^ ERROR use of unstable library feature `rustc_contracts_internals` + core::contracts::build_check_ensures(|_: &()| true); + //~^ ERROR use of unstable library feature `rustc_contracts_internals` + + // ast extensions are guarded by rustc_contracts_internals feature gate + fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 } + //~^ ERROR contract internal machinery is for internal use only + fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 } + //~^ ERROR contract internal machinery is for internal use only +} diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr new file mode 100644 index 0000000000000..2acd03b9a358f --- /dev/null +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr @@ -0,0 +1,73 @@ +error[E0658]: contract internal machinery is for internal use only + --> $DIR/internal-feature-gating.rs:19:51 + | +LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 } + | ^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` 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[E0658]: contract internal machinery is for internal use only + --> $DIR/internal-feature-gating.rs:21:50 + | +LL | fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 } + | ^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` 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[E0658]: use of unstable library feature `rustc_contracts_internals` + --> $DIR/internal-feature-gating.rs:5:5 + | +LL | core::intrinsics::contract_checks(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` 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[E0658]: use of unstable library feature `rustc_contracts_internals` + --> $DIR/internal-feature-gating.rs:7:5 + | +LL | core::intrinsics::contract_check_requires(|| true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` 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[E0658]: use of unstable library feature `rustc_contracts_internals` + --> $DIR/internal-feature-gating.rs:9:5 + | +LL | core::intrinsics::contract_check_ensures(&1, |_|true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` 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[E0658]: use of unstable library feature `rustc_contracts_internals` + --> $DIR/internal-feature-gating.rs:13:5 + | +LL | core::contracts::check_requires(|| true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` 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[E0658]: use of unstable library feature `rustc_contracts_internals` + --> $DIR/internal-feature-gating.rs:15:5 + | +LL | core::contracts::build_check_ensures(|_: &()| true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` 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 7 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-rustc-contracts.rs b/tests/ui/feature-gates/feature-gate-rustc-contracts.rs new file mode 100644 index 0000000000000..d4249c252cda3 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-rustc-contracts.rs @@ -0,0 +1,11 @@ +#![crate_type = "lib"] + +#[core::contracts::requires(x > 0)] +pub fn requires_needs_it(x: i32) { } +//~^^ ERROR use of unstable library feature `rustc_contracts` +//~^^^ ERROR contracts are experimental + +#[core::contracts::ensures(|ret| *ret > 0)] +pub fn ensures_needs_it() -> i32 { 10 } +//~^^ ERROR use of unstable library feature `rustc_contracts` +//~^^^ ERROR contracts are experimental diff --git a/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr b/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr new file mode 100644 index 0000000000000..eb7777a4a5179 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr @@ -0,0 +1,43 @@ +error[E0658]: use of unstable library feature `rustc_contracts` + --> $DIR/feature-gate-rustc-contracts.rs:3:3 + | +LL | #[core::contracts::requires(x > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts)]` 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[E0658]: use of unstable library feature `rustc_contracts` + --> $DIR/feature-gate-rustc-contracts.rs:8:3 + | +LL | #[core::contracts::ensures(|ret| *ret > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts)]` 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[E0658]: contracts are experimental + --> $DIR/feature-gate-rustc-contracts.rs:3:1 + | +LL | #[core::contracts::requires(x > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts)]` 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[E0658]: contracts are experimental + --> $DIR/feature-gate-rustc-contracts.rs:8:1 + | +LL | #[core::contracts::ensures(|ret| *ret > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts)]` 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 4 previous errors + +For more information about this error, try `rustc --explain E0658`. From 804cce47d96d7b30f3798b51a1377c6697011c54 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 15 Jan 2025 13:54:04 -0800 Subject: [PATCH 21/28] Refactor contract builtin macro + error handling Instead of parsing the different components of a function signature, eagerly look for either the `where` keyword or the function body. - Also address feedback to use `From` instead of `TryFrom` in cranelift contract and ubcheck codegen. --- .../rustc_builtin_macros/src/contracts.rs | 142 +++++++++--------- compiler/rustc_codegen_cranelift/src/base.rs | 4 +- .../contract-annotation-limitations.rs | 27 ++++ .../contract-annotation-limitations.stderr | 14 ++ .../contracts/contract-attributes-generics.rs | 70 +++++++++ .../disallow-contract-annotation-on-non-fn.rs | 52 +++++++ ...allow-contract-annotation-on-non-fn.stderr | 44 ++++++ 7 files changed, 280 insertions(+), 73 deletions(-) create mode 100644 tests/ui/contracts/contract-annotation-limitations.rs create mode 100644 tests/ui/contracts/contract-annotation-limitations.stderr create mode 100644 tests/ui/contracts/contract-attributes-generics.rs create mode 100644 tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs create mode 100644 tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs index be7f276cdaa93..fbdd8af99542d 100644 --- a/compiler/rustc_builtin_macros/src/contracts.rs +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -35,90 +35,90 @@ impl AttrProcMacro for ExpandEnsures { } } -fn expand_injecting_circa_where_clause( +/// Expand the function signature to include the contract clause. +/// +/// The contracts clause will be injected before the function body and the optional where clause. +/// For that, we search for the body / where token, and invoke the `inject` callback to generate the +/// contract clause in the right place. +/// +// FIXME: this kind of manual token tree munging does not have significant precedent among +// rustc builtin macros, probably because most builtin macros use direct AST manipulation to +// accomplish similar goals. But since our attributes need to take arbitrary expressions, and +// our attribute infrastructure does not yet support mixing a token-tree annotation with an AST +// annotated, we end up doing token tree manipulation. +fn expand_contract_clause( ecx: &mut ExtCtxt<'_>, attr_span: Span, annotated: TokenStream, - inject: impl FnOnce(&mut Vec) -> Result<(), ErrorGuaranteed>, + inject: impl FnOnce(&mut TokenStream) -> Result<(), ErrorGuaranteed>, ) -> Result { - let mut new_tts = Vec::with_capacity(annotated.len()); + let mut new_tts = TokenStream::default(); let mut cursor = annotated.iter(); - // Find the `fn name(x:X,...)` and inject the AST contract forms right after - // the formal parameters (and return type if any). - while let Some(tt) = cursor.next() { - new_tts.push(tt.clone()); - if let TokenTree::Token(tok, _) = tt - && tok.is_ident_named(kw::Fn) - { - break; - } + let is_kw = |tt: &TokenTree, sym: Symbol| { + if let TokenTree::Token(token, _) = tt { token.is_ident_named(sym) } else { false } + }; + + // Find the `fn` keyword to check if this is a function. + if cursor + .find(|tt| { + new_tts.push_tree((*tt).clone()); + is_kw(tt, kw::Fn) + }) + .is_none() + { + return Err(ecx + .sess + .dcx() + .span_err(attr_span, "contract annotations can only be used on functions")); } - // Found the `fn` keyword, now find the formal parameters. - // - // FIXME: can this fail if you have parentheticals in a generics list, like `fn foo Y>` ? - while let Some(tt) = cursor.next() { - new_tts.push(tt.clone()); - - if let TokenTree::Delimited(_, _, token::Delimiter::Parenthesis, _) = tt { - break; - } - if let TokenTree::Token(token::Token { kind: token::TokenKind::Semi, .. }, _) = tt { - panic!("contract attribute applied to fn without parameter list."); + // Found the `fn` keyword, now find either the `where` token or the function body. + let next_tt = loop { + let Some(tt) = cursor.next() else { + return Err(ecx.sess.dcx().span_err( + attr_span, + "contract annotations is only supported in functions with bodies", + )); + }; + // If `tt` is the last element. Check if it is the function body. + if cursor.peek().is_none() { + if let TokenTree::Delimited(_, _, token::Delimiter::Brace, _) = tt { + break tt; + } else { + return Err(ecx.sess.dcx().span_err( + attr_span, + "contract annotations is only supported in functions with bodies", + )); + } } - } - // There *might* be a return type declaration (and figuring out where that ends would require - // parsing an arbitrary type expression, e.g. `-> Foo` - // - // Instead of trying to figure that out, scan ahead and look for the first occurence of a - // `where`, a `{ ... }`, or a `;`. - // - // FIXME: this might still fall into a trap for something like `-> Ctor`. I - // *think* such cases must be under a Delimited (e.g. `[T; { N }]` or have the braced form - // prefixed by e.g. `const`, so we should still be able to filter them out without having to - // parse the type expression itself. But rather than try to fix things with hacks like that, - // time might be better spent extending the attribute expander to suport tt-annotation atop - // ast-annotated, which would be an elegant way to sidestep all of this. - let mut opt_next_tt = cursor.next(); - while let Some(next_tt) = opt_next_tt { - if let TokenTree::Token(tok, _) = next_tt - && tok.is_ident_named(kw::Where) - { - break; - } - if let TokenTree::Delimited(_, _, token::Delimiter::Brace, _) = next_tt { - break; - } - if let TokenTree::Token(token::Token { kind: token::TokenKind::Semi, .. }, _) = next_tt { - break; + if is_kw(tt, kw::Where) { + break tt; } - - // for anything else, transcribe the tt and keep looking. - new_tts.push(next_tt.clone()); - opt_next_tt = cursor.next(); - } + new_tts.push_tree(tt.clone()); + }; // At this point, we've transcribed everything from the `fn` through the formal parameter list // and return type declaration, (if any), but `tt` itself has *not* been transcribed. // // Now inject the AST contract form. // - // FIXME: this kind of manual token tree munging does not have significant precedent among - // rustc builtin macros, probably because most builtin macros use direct AST manipulation to - // accomplish similar goals. But since our attributes need to take arbitrary expressions, and - // our attribute infrastructure does not yet support mixing a token-tree annotation with an AST - // annotated, we end up doing token tree manipulation. inject(&mut new_tts)?; - // Above we injected the internal AST requires/ensures contruct. Now copy over all the other + // Above we injected the internal AST requires/ensures construct. Now copy over all the other // token trees. - if let Some(tt) = opt_next_tt { - new_tts.push(tt.clone()); - } + new_tts.push_tree(next_tt.clone()); while let Some(tt) = cursor.next() { - new_tts.push(tt.clone()); + new_tts.push_tree(tt.clone()); + if cursor.peek().is_none() + && !matches!(tt, TokenTree::Delimited(_, _, token::Delimiter::Brace, _)) + { + return Err(ecx.sess.dcx().span_err( + attr_span, + "contract annotations is only supported in functions with bodies", + )); + } } // Record the span as a contract attribute expansion. @@ -126,7 +126,7 @@ fn expand_injecting_circa_where_clause( // which is gated via `rustc_contracts_internals`. ecx.psess().contract_attribute_spans.push(attr_span); - Ok(TokenStream::new(new_tts)) + Ok(new_tts) } fn expand_requires_tts( @@ -135,16 +135,16 @@ fn expand_requires_tts( annotation: TokenStream, annotated: TokenStream, ) -> Result { - expand_injecting_circa_where_clause(_ecx, attr_span, annotated, |new_tts| { - new_tts.push(TokenTree::Token( + expand_contract_clause(_ecx, attr_span, annotated, |new_tts| { + new_tts.push_tree(TokenTree::Token( token::Token::from_ast_ident(Ident::new(kw::RustcContractRequires, attr_span)), Spacing::Joint, )); - new_tts.push(TokenTree::Token( + new_tts.push_tree(TokenTree::Token( token::Token::new(token::TokenKind::OrOr, attr_span), Spacing::Alone, )); - new_tts.push(TokenTree::Delimited( + new_tts.push_tree(TokenTree::Delimited( DelimSpan::from_single(attr_span), DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), token::Delimiter::Parenthesis, @@ -160,12 +160,12 @@ fn expand_ensures_tts( annotation: TokenStream, annotated: TokenStream, ) -> Result { - expand_injecting_circa_where_clause(_ecx, attr_span, annotated, |new_tts| { - new_tts.push(TokenTree::Token( + expand_contract_clause(_ecx, attr_span, annotated, |new_tts| { + new_tts.push_tree(TokenTree::Token( token::Token::from_ast_ident(Ident::new(kw::RustcContractEnsures, attr_span)), Spacing::Joint, )); - new_tts.push(TokenTree::Delimited( + new_tts.push_tree(TokenTree::Delimited( DelimSpan::from_single(attr_span), DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), token::Delimiter::Parenthesis, diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index de2ce1768fa18..a2b9e5712e50b 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -868,7 +868,7 @@ fn codegen_stmt<'tcx>( NullOp::UbChecks => { let val = fx.tcx.sess.ub_checks(); let val = CValue::by_val( - fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()), + fx.bcx.ins().iconst(types::I8, i64::from(val)), fx.layout_of(fx.tcx.types.bool), ); lval.write_cvalue(fx, val); @@ -877,7 +877,7 @@ fn codegen_stmt<'tcx>( 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.bcx.ins().iconst(types::I8, i64::from(val)), fx.layout_of(fx.tcx.types.bool), ); lval.write_cvalue(fx, val); diff --git a/tests/ui/contracts/contract-annotation-limitations.rs b/tests/ui/contracts/contract-annotation-limitations.rs new file mode 100644 index 0000000000000..f01d526e3f70e --- /dev/null +++ b/tests/ui/contracts/contract-annotation-limitations.rs @@ -0,0 +1,27 @@ +//! Test for some of the existing limitations and the current error messages. +//! Some of these limitations may be removed in the future. + +#![feature(rustc_contracts)] +#![allow(dead_code)] + +/// Represent a 5-star system. +struct Stars(u8); + +impl Stars { + fn is_valid(&self) -> bool { + self.0 <= 5 + } +} + +trait ParseStars { + #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + //~^ ERROR contract annotations is only supported in functions with bodies + fn parse_string(input: String) -> Option; + + #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + //~^ ERROR contract annotations is only supported in functions with bodies + fn parse(input: T) -> Option where T: for<'a> Into<&'a str>; +} + +fn main() { +} diff --git a/tests/ui/contracts/contract-annotation-limitations.stderr b/tests/ui/contracts/contract-annotation-limitations.stderr new file mode 100644 index 0000000000000..25b01744aac8e --- /dev/null +++ b/tests/ui/contracts/contract-annotation-limitations.stderr @@ -0,0 +1,14 @@ +error: contract annotations is only supported in functions with bodies + --> $DIR/contract-annotation-limitations.rs:17:5 + | +LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations is only supported in functions with bodies + --> $DIR/contract-annotation-limitations.rs:21:5 + | +LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/contracts/contract-attributes-generics.rs b/tests/ui/contracts/contract-attributes-generics.rs new file mode 100644 index 0000000000000..87088ce9de2ce --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.rs @@ -0,0 +1,70 @@ +//! Test that contracts can be applied to generic functions. + +//@ revisions: unchk_pass chk_pass chk_fail_pre chk_fail_post chk_const_fail +// +//@ [unchk_pass] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +//@ [chk_const_fail] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes +//@ [chk_const_fail] compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +use std::ops::Sub; + +/// Dummy fn contract that precondition fails for val < 0, and post-condition fail for val == 1 +#[core::contracts::requires(val > 0u8.into())] +#[core::contracts::ensures(|ret| *ret > 0u8.into())] +fn decrement(val: T) -> T +where T: PartialOrd + Sub + From +{ + val - 1u8.into() +} + +/// Create a structure that takes a constant parameter. +#[allow(dead_code)] +struct Capped(usize); + +/// Now declare a function to create stars which shouldn't exceed 5 stars. +// Add redundant braces to ensure the built-in macro can handle this syntax. +#[allow(unused_braces)] +#[core::contracts::requires(num <= 5)] +unsafe fn stars_unchecked(num: usize) -> Capped<{ 5 }> { + Capped(num) +} + + +fn main() { + check_decrement(); + check_stars(); +} + +fn check_stars() { + // This should always pass. + let _ = unsafe { stars_unchecked(3) }; + + // This violates the contract. + #[cfg(any(unchk_pass, chk_const_fail))] + let _ = unsafe { stars_unchecked(10) }; +} + +fn check_decrement() { + // This should always pass + assert_eq!(decrement(10u8), 9u8); + + // This should fail requires but pass with no contract check. + #[cfg(any(unchk_pass, chk_fail_pre))] + assert_eq!(decrement(-2i128), -3i128); + + // This should fail ensures but pass with no contract check. + #[cfg(any(unchk_pass, chk_fail_post))] + assert_eq!(decrement(1i32), 0i32); +} diff --git a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs new file mode 100644 index 0000000000000..76ed30e856462 --- /dev/null +++ b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs @@ -0,0 +1,52 @@ +//! Checks for compilation errors related to adding contracts to non-function items. + +#![feature(rustc_contracts)] +#![allow(dead_code)] + +#[core::contracts::requires(true)] +//~^ ERROR contract annotations can only be used on functions +struct Dummy(usize); + +#[core::contracts::ensures(|v| v == 100)] +//~^ ERROR contract annotations can only be used on functions +const MAX_VAL: usize = 100; + +// FIXME: Improve the error message here. The macro thinks this is a function. +#[core::contracts::ensures(|v| v == 100)] +//~^ ERROR contract annotations is only supported in functions with bodies +type NewDummy = fn(usize) -> Dummy; + +#[core::contracts::ensures(|v| v == 100)] +//~^ ERROR contract annotations is only supported in functions with bodies +const NEW_DUMMY_FN : fn(usize) -> Dummy = || { Dummy(0) }; + +#[core::contracts::requires(true)] +//~^ ERROR contract annotations can only be used on functions +impl Dummy { + + // This should work + #[core::contracts::ensures(|ret| ret.0 == v)] + fn new(v: usize) -> Dummy { + Dummy(v) + } +} + +#[core::contracts::ensures(|dummy| dummy.0 > 0)] +//~^ ERROR contract annotations can only be used on functions +impl From for Dummy { + // This should work. + #[core::contracts::ensures(|ret| ret.0 == v)] + fn from(value: usize) -> Self { + Dummy::new(value) + } +} + +/// You should not be able to annotate a trait either. +#[core::contracts::requires(true)] +//~^ ERROR contract annotations can only be used on functions +pub trait DummyBuilder { + fn build() -> Dummy; +} + +fn main() { +} diff --git a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr new file mode 100644 index 0000000000000..4d6d23340ac0e --- /dev/null +++ b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr @@ -0,0 +1,44 @@ +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:6:1 + | +LL | #[core::contracts::requires(true)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:10:1 + | +LL | #[core::contracts::ensures(|v| v == 100)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations is only supported in functions with bodies + --> $DIR/disallow-contract-annotation-on-non-fn.rs:15:1 + | +LL | #[core::contracts::ensures(|v| v == 100)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations is only supported in functions with bodies + --> $DIR/disallow-contract-annotation-on-non-fn.rs:19:1 + | +LL | #[core::contracts::ensures(|v| v == 100)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:23:1 + | +LL | #[core::contracts::requires(true)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:34:1 + | +LL | #[core::contracts::ensures(|dummy| dummy.0 > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:45:1 + | +LL | #[core::contracts::requires(true)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + From 2bb1464cb6b46d175f92943cb0f9ab534e6cc6eb Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 17 Jan 2025 14:49:10 -0800 Subject: [PATCH 22/28] Improve contracts intrisics and remove wrapper function 1. Document the new intrinsics. 2. Make the intrinsics actually check the contract if enabled, and remove `contract::check_requires` function. 3. Use panic with no unwind in case contract is using to check for safety, we probably don't want to unwind. Following the same reasoning as UB checks. --- .../rustc_hir_analysis/src/check/intrinsic.rs | 10 ++--- library/core/src/contracts.rs | 27 +++----------- library/core/src/intrinsics/mod.rs | 22 +++++++++-- .../internal_machinery/contract-intrinsics.rs | 37 +++++++++++++------ .../internal_machinery/contract-lang-items.rs | 12 +----- .../internal-feature-gating.rs | 3 -- .../internal-feature-gating.stderr | 18 ++------- 7 files changed, 57 insertions(+), 72 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 6c0cebccefd95..e641fb0fb62e6 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -223,17 +223,15 @@ pub fn check_intrinsic_type( }; (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 + // contract_check_ensures::<'a, Ret, C>(&'a Ret, C) // where C: impl Fn(&'a Ret) -> bool, // - // so: two type params, one lifetime param, 0 const params, two inputs, returns boolean + // so: two type params, one lifetime param, 0 const params, two inputs, no return 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) + (2, 1, 0, vec![ref_ret, param(2)], tcx.types.unit, hir::Safety::Safe) } else { let safety = intrinsic_operation_unsafety(tcx, intrinsic_id); let (n_tps, n_cts, inputs, output) = match intrinsic_name { @@ -628,7 +626,7 @@ pub fn check_intrinsic_type( // contract_checks() -> bool sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool), // contract_check_requires::(C) -> bool, where C: impl Fn() -> bool - sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.bool), + sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.unit), sym::simd_eq | sym::simd_ne diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs index b155dbc213ee8..0668cacb92c60 100644 --- a/library/core/src/contracts.rs +++ b/library/core/src/contracts.rs @@ -1,38 +1,21 @@ //! Unstable module containing the unstable contracts lang items and attribute macros. +#![cfg(not(bootstrap))] -#[cfg(not(bootstrap))] -pub use crate::macros::builtin::contracts_ensures as ensures; -#[cfg(not(bootstrap))] -pub use crate::macros::builtin::contracts_requires as requires; - -/// Emitted by rustc as a desugaring of `#[requires(PRED)] fn foo(x: X) { ... }` -/// into: `fn foo(x: X) { check_requires(|| PRED) ... }` -#[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] -#[lang = "contract_check_requires"] -#[track_caller] -pub fn check_requires bool>(c: C) { - if core::intrinsics::contract_checks() { - assert!(core::intrinsics::contract_check_requires(c), "failed requires check"); - } -} +pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_requires as requires}; /// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }` /// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }` /// (including the implicit return of the tail expression, if any). -#[cfg(not(bootstrap))] #[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] #[lang = "contract_build_check_ensures"] #[track_caller] -pub fn build_check_ensures(c: C) -> impl (FnOnce(Ret) -> Ret) + Copy +pub fn build_check_ensures(cond: C) -> impl (Fn(Ret) -> Ret) + Copy where - C: for<'a> FnOnce(&'a Ret) -> bool + Copy + 'static, + C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static, { #[track_caller] move |ret| { - if core::intrinsics::contract_checks() { - assert!(core::intrinsics::contract_check_ensures(&ret, c), "failed ensures check"); - } + crate::intrinsics::contract_check_ensures(&ret, cond); ret } } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index a7f0f09f0c6b3..14f8645d6f1e5 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -4062,18 +4062,32 @@ pub const fn contract_checks() -> bool { false } +/// Check if the pre-condition `cond` has been met. +/// +/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition +/// returns false. #[cfg(not(bootstrap))] #[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] +#[lang = "contract_check_requires"] #[rustc_intrinsic] -pub fn contract_check_requires bool>(c: C) -> bool { - c() +pub fn contract_check_requires bool>(cond: C) { + if contract_checks() && !cond() { + // Emit no unwind panic in case this was a safety requirement. + crate::panicking::panic_nounwind("failed requires check"); + } } +/// Check if the post-condition `cond` has been met. +/// +/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition +/// returns false. #[cfg(not(bootstrap))] #[unstable(feature = "rustc_contracts_internals", issue = "133866" /* 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) +pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) { + if contract_checks() && !cond(ret) { + crate::panicking::panic_nounwind("failed ensures check"); + } } /// The intrinsic will return the size stored in that vtable. diff --git a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs index 2e1be01e7cabc..8c70c1a85f6d3 100644 --- a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs +++ b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs @@ -1,23 +1,36 @@ -//@ run-pass -//@ revisions: yes no none -//@ [yes] compile-flags: -Zcontract-checks=yes -//@ [no] compile-flags: -Zcontract-checks=no +//@ revisions: default unchk_pass chk_pass chk_fail_ensures chk_fail_requires +// +//@ [default] run-pass +//@ [unchk_pass] run-pass +//@ [chk_pass] run-pass +//@ [chk_fail_requires] run-fail +//@ [chk_fail_ensures] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_requires] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_ensures] compile-flags: -Zcontract-checks=yes #![feature(cfg_contract_checks, rustc_contracts_internals, core_intrinsics)] fn main() { - #[cfg(none)] // default: disabled + #[cfg(any(default, unchk_pass))] // default: disabled assert_eq!(core::intrinsics::contract_checks(), false); - #[cfg(yes)] // explicitly enabled + #[cfg(chk_pass)] // explicitly enabled assert_eq!(core::intrinsics::contract_checks(), true); - #[cfg(no)] // explicitly disabled - assert_eq!(core::intrinsics::contract_checks(), false); + // always pass + core::intrinsics::contract_check_requires(|| true); - assert_eq!(core::intrinsics::contract_check_requires(|| true), true); - assert_eq!(core::intrinsics::contract_check_requires(|| false), false); + // fail if enabled + #[cfg(any(default, unchk_pass, chk_fail_requires))] + core::intrinsics::contract_check_requires(|| 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); + // Always pass + core::intrinsics::contract_check_ensures(&1, doubles_to_two); + + // Fail if enabled + #[cfg(any(default, unchk_pass, chk_fail_ensures))] + core::intrinsics::contract_check_ensures(&2, doubles_to_two); } diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.rs b/tests/ui/contracts/internal_machinery/contract-lang-items.rs index 5af467485b10e..ff569e011f206 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.rs +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.rs @@ -1,27 +1,21 @@ -//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +//@ revisions: unchk_pass unchk_fail_post chk_pass chk_fail_post // //@ [unchk_pass] run-pass -//@ [unchk_fail_pre] run-pass //@ [unchk_fail_post] run-pass //@ [chk_pass] run-pass // -//@ [chk_fail_pre] run-fail //@ [chk_fail_post] run-fail // //@ [unchk_pass] compile-flags: -Zcontract-checks=no -//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no //@ [unchk_fail_post] compile-flags: -Zcontract-checks=no // //@ [chk_pass] compile-flags: -Zcontract-checks=yes -//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes #![feature(rustc_contracts)] // to access core::contracts #![feature(rustc_contracts_internals)] // to access check_requires lang item fn foo(x: Baz) -> i32 { - core::contracts::check_requires(|| x.baz > 0); - let injected_checker = { core::contracts::build_check_ensures(|ret| *ret > 100) }; @@ -36,13 +30,9 @@ struct Baz { baz: i32 } const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; #[cfg(any(unchk_fail_post, chk_fail_post))] const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; -#[cfg(any(unchk_fail_pre, chk_fail_pre))] -const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; fn main() { assert_eq!(foo(BAZ_PASS_PRE_POST), 150); - #[cfg(any(unchk_fail_pre, chk_fail_pre))] - foo(BAZ_FAIL_PRE); #[cfg(any(unchk_fail_post, chk_fail_post))] foo(BAZ_FAIL_POST); } diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs index 7b5f17679421f..ee9edf3ebb0c2 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs @@ -9,9 +9,6 @@ fn main() { core::intrinsics::contract_check_ensures(&1, |_|true); //~^ ERROR use of unstable library feature `rustc_contracts_internals` - // lang items are guarded by rustc_contracts_internals feature gate. - core::contracts::check_requires(|| true); - //~^ ERROR use of unstable library feature `rustc_contracts_internals` core::contracts::build_check_ensures(|_: &()| true); //~^ ERROR use of unstable library feature `rustc_contracts_internals` diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr index 2acd03b9a358f..5f9263e03e85a 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr @@ -1,5 +1,5 @@ error[E0658]: contract internal machinery is for internal use only - --> $DIR/internal-feature-gating.rs:19:51 + --> $DIR/internal-feature-gating.rs:16:51 | LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 } | ^^^^^^^^^ @@ -9,7 +9,7 @@ LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: contract internal machinery is for internal use only - --> $DIR/internal-feature-gating.rs:21:50 + --> $DIR/internal-feature-gating.rs:18:50 | LL | fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 } | ^^^^^^^^^^ @@ -49,17 +49,7 @@ LL | core::intrinsics::contract_check_ensures(&1, |_|true); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `rustc_contracts_internals` - --> $DIR/internal-feature-gating.rs:13:5 - | -LL | core::contracts::check_requires(|| true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` 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[E0658]: use of unstable library feature `rustc_contracts_internals` - --> $DIR/internal-feature-gating.rs:15:5 + --> $DIR/internal-feature-gating.rs:12:5 | LL | core::contracts::build_check_ensures(|_: &()| true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -68,6 +58,6 @@ LL | core::contracts::build_check_ensures(|_: &()| true); = help: add `#![feature(rustc_contracts_internals)]` 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 7 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0658`. From 2c4923e6bc9608557f0bc59f975006e590fd337d Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 30 Jan 2025 13:09:14 -0800 Subject: [PATCH 23/28] Update test output to include check_contracts cfg This is now a valid expected value. --- tests/ui/check-cfg/cargo-build-script.stderr | 2 +- tests/ui/check-cfg/cargo-feature.none.stderr | 2 +- tests/ui/check-cfg/cargo-feature.some.stderr | 2 +- tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr | 2 +- tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr | 2 +- tests/ui/check-cfg/exhaustive-names-values.feature.stderr | 2 +- tests/ui/check-cfg/exhaustive-names-values.full.stderr | 2 +- tests/ui/check-cfg/mix.stderr | 2 +- tests/ui/check-cfg/raw-keywords.edition2015.stderr | 2 +- tests/ui/check-cfg/raw-keywords.edition2021.stderr | 2 +- tests/ui/check-cfg/report-in-external-macros.cargo.stderr | 2 +- tests/ui/check-cfg/report-in-external-macros.rustc.stderr | 2 +- tests/ui/check-cfg/well-known-names.stderr | 1 + 13 files changed, 13 insertions(+), 12 deletions(-) diff --git a/tests/ui/check-cfg/cargo-build-script.stderr b/tests/ui/check-cfg/cargo-build-script.stderr index df0bc47571c72..03a7156a4d69e 100644 --- a/tests/ui/check-cfg/cargo-build-script.stderr +++ b/tests/ui/check-cfg/cargo-build-script.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `has_foo` LL | #[cfg(has_foo)] | ^^^^^^^ | - = help: expected names are: `has_bar` and 30 more + = help: expected names are: `has_bar` and 31 more = help: consider using a Cargo feature instead = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: [lints.rust] diff --git a/tests/ui/check-cfg/cargo-feature.none.stderr b/tests/ui/check-cfg/cargo-feature.none.stderr index 58813a1f6770c..b83d1794984de 100644 --- a/tests/ui/check-cfg/cargo-feature.none.stderr +++ b/tests/ui/check-cfg/cargo-feature.none.stderr @@ -25,7 +25,7 @@ warning: unexpected `cfg` condition name: `tokio_unstable` LL | #[cfg(tokio_unstable)] | ^^^^^^^^^^^^^^ | - = help: expected names are: `docsrs`, `feature`, and `test` and 30 more + = help: expected names are: `docsrs`, `feature`, and `test` and 31 more = help: consider using a Cargo feature instead = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: [lints.rust] diff --git a/tests/ui/check-cfg/cargo-feature.some.stderr b/tests/ui/check-cfg/cargo-feature.some.stderr index 5a12be8133871..2cddcbbcd7f9e 100644 --- a/tests/ui/check-cfg/cargo-feature.some.stderr +++ b/tests/ui/check-cfg/cargo-feature.some.stderr @@ -25,7 +25,7 @@ warning: unexpected `cfg` condition name: `tokio_unstable` LL | #[cfg(tokio_unstable)] | ^^^^^^^^^^^^^^ | - = help: expected names are: `CONFIG_NVME`, `docsrs`, `feature`, and `test` and 30 more + = help: expected names are: `CONFIG_NVME`, `docsrs`, `feature`, and `test` and 31 more = help: consider using a Cargo feature instead = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: [lints.rust] diff --git a/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr b/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr index 7c276c581707e..68e1259dbb842 100644 --- a/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr +++ b/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `value` LL | #[cfg(value)] | ^^^^^ | - = help: expected names are: `bar`, `bee`, `cow`, and `foo` and 30 more + = help: expected names are: `bar`, `bee`, `cow`, and `foo` and 31 more = help: to expect this configuration use `--check-cfg=cfg(value)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr b/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr index 9687a043e8322..138c7fc758494 100644 --- a/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr +++ b/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `my_value` LL | #[cfg(my_value)] | ^^^^^^^^ | - = help: expected names are: `bar` and `foo` and 30 more + = help: expected names are: `bar` and `foo` and 31 more = help: to expect this configuration use `--check-cfg=cfg(my_value)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr index 10302f0a7e46a..af66cbd818946 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/exhaustive-names-values.full.stderr b/tests/ui/check-cfg/exhaustive-names-values.full.stderr index 10302f0a7e46a..af66cbd818946 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.full.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.full.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/mix.stderr b/tests/ui/check-cfg/mix.stderr index 033aaef848f74..be4d7c7727636 100644 --- a/tests/ui/check-cfg/mix.stderr +++ b/tests/ui/check-cfg/mix.stderr @@ -44,7 +44,7 @@ warning: unexpected `cfg` condition name: `uu` LL | #[cfg_attr(uu, unix)] | ^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = help: to expect this configuration use `--check-cfg=cfg(uu)` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/raw-keywords.edition2015.stderr b/tests/ui/check-cfg/raw-keywords.edition2015.stderr index f19ded9cb6715..8ca33e088fc94 100644 --- a/tests/ui/check-cfg/raw-keywords.edition2015.stderr +++ b/tests/ui/check-cfg/raw-keywords.edition2015.stderr @@ -14,7 +14,7 @@ warning: unexpected `cfg` condition name: `r#false` LL | #[cfg(r#false)] | ^^^^^^^ | - = help: expected names are: `async`, `edition2015`, `edition2021`, and `r#true` and 30 more + = help: expected names are: `async`, `edition2015`, `edition2021`, and `r#true` and 31 more = help: to expect this configuration use `--check-cfg=cfg(r#false)` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/raw-keywords.edition2021.stderr b/tests/ui/check-cfg/raw-keywords.edition2021.stderr index 6096148a259b0..cce55720bdd18 100644 --- a/tests/ui/check-cfg/raw-keywords.edition2021.stderr +++ b/tests/ui/check-cfg/raw-keywords.edition2021.stderr @@ -14,7 +14,7 @@ warning: unexpected `cfg` condition name: `r#false` LL | #[cfg(r#false)] | ^^^^^^^ | - = help: expected names are: `r#async`, `edition2015`, `edition2021`, and `r#true` and 30 more + = help: expected names are: `r#async`, `edition2015`, `edition2021`, and `r#true` and 31 more = help: to expect this configuration use `--check-cfg=cfg(r#false)` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr index a6584d777a3a6..989a01f224412 100644 --- a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr +++ b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `my_lib_cfg` LL | cfg_macro::my_lib_macro!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro` crate for guidance on how handle this unexpected cfg = help: the macro `cfg_macro::my_lib_macro` may come from an old version of the `cfg_macro` crate, try updating your dependency with `cargo update -p cfg_macro` diff --git a/tests/ui/check-cfg/report-in-external-macros.rustc.stderr b/tests/ui/check-cfg/report-in-external-macros.rustc.stderr index 914b5a0efe366..95d10e014f33b 100644 --- a/tests/ui/check-cfg/report-in-external-macros.rustc.stderr +++ b/tests/ui/check-cfg/report-in-external-macros.rustc.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `my_lib_cfg` LL | cfg_macro::my_lib_macro!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro` crate for guidance on how handle this unexpected cfg = help: to expect this configuration use `--check-cfg=cfg(my_lib_cfg)` diff --git a/tests/ui/check-cfg/well-known-names.stderr b/tests/ui/check-cfg/well-known-names.stderr index 4ff90261158bc..000315443f810 100644 --- a/tests/ui/check-cfg/well-known-names.stderr +++ b/tests/ui/check-cfg/well-known-names.stderr @@ -5,6 +5,7 @@ LL | #[cfg(list_all_well_known_cfgs)] | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: expected names are: `clippy` +`contract_checks` `debug_assertions` `doc` `doctest` From ddbf54b67d9befcf1fb90613d2a6f7f6aa03141e Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 30 Jan 2025 17:06:09 -0800 Subject: [PATCH 24/28] Rename rustc_contract to contract This has now been approved as a language feature and no longer needs a `rustc_` prefix. Also change the `contracts` feature to be marked as incomplete and `contracts_internals` as internal. --- compiler/rustc_ast_passes/src/feature_gate.rs | 4 +- .../rustc_builtin_macros/src/contracts.rs | 6 +-- compiler/rustc_feature/src/unstable.rs | 10 ++--- compiler/rustc_parse/src/parser/generics.rs | 12 ++--- compiler/rustc_parse/src/parser/token_type.rs | 16 +++---- compiler/rustc_span/src/symbol.rs | 8 ++-- library/core/src/contracts.rs | 2 +- library/core/src/intrinsics/mod.rs | 8 ++-- library/core/src/lib.rs | 2 +- library/core/src/macros/mod.rs | 8 ++-- .../contract-annotation-limitations.rs | 3 +- .../contract-annotation-limitations.stderr | 15 +++++-- ...-attributes-generics.chk_const_fail.stderr | 11 +++++ ...t-attributes-generics.chk_fail_post.stderr | 11 +++++ ...ct-attributes-generics.chk_fail_pre.stderr | 11 +++++ ...ntract-attributes-generics.chk_pass.stderr | 11 +++++ .../contracts/contract-attributes-generics.rs | 3 +- ...ract-attributes-generics.unchk_pass.stderr | 11 +++++ ...tract-attributes-nest.chk_fail_post.stderr | 11 +++++ ...ntract-attributes-nest.chk_fail_pre.stderr | 11 +++++ .../contract-attributes-nest.chk_pass.stderr | 11 +++++ .../ui/contracts/contract-attributes-nest.rs | 3 +- ...act-attributes-nest.unchk_fail_post.stderr | 11 +++++ ...ract-attributes-nest.unchk_fail_pre.stderr | 11 +++++ ...contract-attributes-nest.unchk_pass.stderr | 11 +++++ ...tract-attributes-tail.chk_fail_post.stderr | 11 +++++ ...ntract-attributes-tail.chk_fail_pre.stderr | 11 +++++ .../contract-attributes-tail.chk_pass.stderr | 11 +++++ .../ui/contracts/contract-attributes-tail.rs | 3 +- ...act-attributes-tail.unchk_fail_post.stderr | 11 +++++ ...ract-attributes-tail.unchk_fail_pre.stderr | 11 +++++ ...contract-attributes-tail.unchk_pass.stderr | 11 +++++ .../contract-captures-via-closure-copy.rs | 3 +- .../contract-captures-via-closure-copy.stderr | 11 +++++ .../contract-captures-via-closure-noncopy.rs | 3 +- ...ntract-captures-via-closure-noncopy.stderr | 23 +++++++--- ...-ensures-early-fn-exit.chk_fail_ret.stderr | 11 +++++ ...-ensures-early-fn-exit.chk_fail_try.stderr | 11 +++++ ...ensures-early-fn-exit.chk_fail_yeet.stderr | 11 +++++ ...acts-ensures-early-fn-exit.chk_pass.stderr | 11 +++++ .../contracts-ensures-early-fn-exit.rs | 3 +- ...ts-ensures-early-fn-exit.unchk_pass.stderr | 11 +++++ ...s-ensures-is-not-inherited-when-nesting.rs | 3 +- ...sures-is-not-inherited-when-nesting.stderr | 11 +++++ ...-requires-is-not-inherited-when-nesting.rs | 3 +- ...uires-is-not-inherited-when-nesting.stderr | 11 +++++ .../disallow-contract-annotation-on-non-fn.rs | 3 +- ...allow-contract-annotation-on-non-fn.stderr | 25 +++++++---- .../contract-ast-extensions-nest.rs | 6 +-- .../contract-ast-extensions-tail.rs | 6 +-- .../internal_machinery/contract-intrinsics.rs | 2 +- .../contract-lang-items.chk_fail_post.stderr | 11 +++++ .../contract-lang-items.chk_pass.stderr | 11 +++++ .../internal_machinery/contract-lang-items.rs | 5 ++- ...contract-lang-items.unchk_fail_post.stderr | 11 +++++ .../contract-lang-items.unchk_pass.stderr | 11 +++++ ...g-ensures-is-not-inherited-when-nesting.rs | 4 +- ...-requires-is-not-inherited-when-nesting.rs | 4 +- .../internal-feature-gating.rs | 18 ++++---- .../internal-feature-gating.stderr | 44 +++++++++---------- .../feature-gate-cfg-contract-checks.stderr | 2 +- .../feature-gates/feature-gate-contracts.rs | 11 +++++ .../feature-gate-contracts.stderr | 43 ++++++++++++++++++ .../feature-gate-rustc-contracts.rs | 11 ----- .../feature-gate-rustc-contracts.stderr | 43 ------------------ 65 files changed, 522 insertions(+), 165 deletions(-) create mode 100644 tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr create mode 100644 tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr create mode 100644 tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr create mode 100644 tests/ui/contracts/contract-attributes-generics.chk_pass.stderr create mode 100644 tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr create mode 100644 tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr create mode 100644 tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr create mode 100644 tests/ui/contracts/contract-attributes-nest.chk_pass.stderr create mode 100644 tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr create mode 100644 tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr create mode 100644 tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr create mode 100644 tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr create mode 100644 tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr create mode 100644 tests/ui/contracts/contract-attributes-tail.chk_pass.stderr create mode 100644 tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr create mode 100644 tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr create mode 100644 tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr create mode 100644 tests/ui/contracts/contract-captures-via-closure-copy.stderr create mode 100644 tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr create mode 100644 tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr create mode 100644 tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr create mode 100644 tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr create mode 100644 tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr create mode 100644 tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr create mode 100644 tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr create mode 100644 tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr create mode 100644 tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr create mode 100644 tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr create mode 100644 tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr create mode 100644 tests/ui/feature-gates/feature-gate-contracts.rs create mode 100644 tests/ui/feature-gates/feature-gate-contracts.stderr delete mode 100644 tests/ui/feature-gates/feature-gate-rustc-contracts.rs delete mode 100644 tests/ui/feature-gates/feature-gate-rustc-contracts.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index a3af942a10f17..62e451fa8764f 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -548,8 +548,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); gate_all!(unsafe_fields, "`unsafe` fields are experimental"); gate_all!(unsafe_binders, "unsafe binder types are experimental"); - gate_all!(rustc_contracts, "contracts are experimental"); - gate_all!(rustc_contracts_internals, "contract internal machinery is for internal use only"); + gate_all!(contracts, "contracts are incomplete"); + gate_all!(contracts_internals, "contract internal machinery is for internal use only"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs index fbdd8af99542d..85a30f7bdc9b4 100644 --- a/compiler/rustc_builtin_macros/src/contracts.rs +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -123,7 +123,7 @@ fn expand_contract_clause( // Record the span as a contract attribute expansion. // This is used later to stop users from using the extended syntax directly - // which is gated via `rustc_contracts_internals`. + // which is gated via `contracts_internals`. ecx.psess().contract_attribute_spans.push(attr_span); Ok(new_tts) @@ -137,7 +137,7 @@ fn expand_requires_tts( ) -> Result { expand_contract_clause(_ecx, attr_span, annotated, |new_tts| { new_tts.push_tree(TokenTree::Token( - token::Token::from_ast_ident(Ident::new(kw::RustcContractRequires, attr_span)), + token::Token::from_ast_ident(Ident::new(kw::ContractRequires, attr_span)), Spacing::Joint, )); new_tts.push_tree(TokenTree::Token( @@ -162,7 +162,7 @@ fn expand_ensures_tts( ) -> Result { expand_contract_clause(_ecx, attr_span, annotated, |new_tts| { new_tts.push_tree(TokenTree::Token( - token::Token::from_ast_ident(Ident::new(kw::RustcContractEnsures, attr_span)), + token::Token::from_ast_ident(Ident::new(kw::ContractEnsures, attr_span)), Spacing::Joint, )); new_tts.push_tree(TokenTree::Delimited( diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 57bcd8c5eca22..5699d4ce3b9b6 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -404,7 +404,7 @@ declare_features! ( /// Allows the use of `#[cfg()]`. (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)), + (unstable, cfg_contract_checks, "CURRENT_RUSTC_VERSION", Some(128044)), /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. (unstable, cfg_overflow_checks, "1.71.0", Some(111466)), /// Provides the relocation model information as cfg entry @@ -447,6 +447,10 @@ declare_features! ( (unstable, const_trait_impl, "1.42.0", Some(67792)), /// Allows the `?` operator in const contexts. (unstable, const_try, "1.56.0", Some(74935)), + /// Allows use of contracts attributes. + (incomplete, contracts, "CURRENT_RUSTC_VERSION", Some(128044)), + /// Allows access to internal machinery used to implement contracts. + (internal, contracts_internals, "CURRENT_RUSTC_VERSION", Some(128044)), /// Allows coroutines to be cloned. (unstable, coroutine_clone, "1.65.0", Some(95360)), /// Allows defining coroutines. @@ -608,10 +612,6 @@ declare_features! ( (unstable, return_type_notation, "1.70.0", Some(109417)), /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), - /// Allows use of contracts attributes. - (unstable, rustc_contracts, "CURRENT_RUSTC_VERSION", Some(133866)), - /// Allows access to internal machinery used to implement contracts. - (unstable, rustc_contracts_internals, "CURRENT_RUSTC_VERSION", Some(133866)), /// Allows use of x86 SHA512, SM3 and SM4 target-features and intrinsics (unstable, sha512_sm_x86, "1.82.0", Some(126624)), /// Allows the use of SIMD types in functions declared in `extern` blocks. diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 14b949dbc3d57..86816819be275 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -297,29 +297,29 @@ impl<'a> Parser<'a> { }) } - /// Parses a rustc-internal fn contract - /// (`rustc_contract_requires(WWW) rustc_contract_ensures(ZZZ)`) + /// Parses an experimental fn contract + /// (`contract_requires(WWW) contract_ensures(ZZZ)`) pub(super) fn parse_contract( &mut self, ) -> PResult<'a, Option>> { let gate = |span| { if self.psess.contract_attribute_spans.contains(span) { // span was generated via a builtin contracts attribute, so gate as end-user visible - self.psess.gated_spans.gate(sym::rustc_contracts, span); + self.psess.gated_spans.gate(sym::contracts, span); } else { // span was not generated via a builtin contracts attribute, so gate as internal machinery - self.psess.gated_spans.gate(sym::rustc_contracts_internals, span); + self.psess.gated_spans.gate(sym::contracts_internals, span); } }; - let requires = if self.eat_keyword_noexpect(exp!(RustcContractRequires).kw) { + let requires = if self.eat_keyword_noexpect(exp!(ContractRequires).kw) { let precond = self.parse_expr()?; gate(precond.span); Some(precond) } else { None }; - let ensures = if self.eat_keyword_noexpect(exp!(RustcContractEnsures).kw) { + let ensures = if self.eat_keyword_noexpect(exp!(ContractEnsures).kw) { let postcond = self.parse_expr()?; gate(postcond.span); Some(postcond) diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index 50f03e72f82dc..40631d9154d37 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -83,6 +83,8 @@ pub enum TokenType { KwCatch, KwConst, KwContinue, + KwContractEnsures, + KwContractRequires, KwCrate, KwDefault, KwDyn, @@ -108,8 +110,6 @@ pub enum TokenType { KwRef, KwReturn, KwReuse, - KwRustcContractEnsures, - KwRustcContractRequires, KwSafe, KwSelfUpper, KwStatic, @@ -219,6 +219,8 @@ impl TokenType { KwCatch, KwConst, KwContinue, + KwContractEnsures, + KwContractRequires, KwCrate, KwDefault, KwDyn, @@ -244,8 +246,6 @@ impl TokenType { KwRef, KwReturn, KwReuse, - KwRustcContractEnsures, - KwRustcContractRequires, KwSafe, KwSelfUpper, KwStatic, @@ -293,6 +293,8 @@ impl TokenType { TokenType::KwCatch => Some(kw::Catch), TokenType::KwConst => Some(kw::Const), TokenType::KwContinue => Some(kw::Continue), + TokenType::KwContractEnsures => Some(kw::ContractEnsures), + TokenType::KwContractRequires => Some(kw::ContractRequires), TokenType::KwCrate => Some(kw::Crate), TokenType::KwDefault => Some(kw::Default), TokenType::KwDyn => Some(kw::Dyn), @@ -318,8 +320,6 @@ impl TokenType { TokenType::KwRef => Some(kw::Ref), TokenType::KwReturn => Some(kw::Return), TokenType::KwReuse => Some(kw::Reuse), - TokenType::KwRustcContractEnsures => Some(kw::RustcContractEnsures), - TokenType::KwRustcContractRequires => Some(kw::RustcContractRequires), TokenType::KwSafe => Some(kw::Safe), TokenType::KwSelfUpper => Some(kw::SelfUpper), TokenType::KwStatic => Some(kw::Static), @@ -525,6 +525,8 @@ macro_rules! exp { (Catch) => { exp!(@kw, Catch, KwCatch) }; (Const) => { exp!(@kw, Const, KwConst) }; (Continue) => { exp!(@kw, Continue, KwContinue) }; + (ContractEnsures) => { exp!(@kw, ContractEnsures, KwContractEnsures) }; + (ContractRequires) => { exp!(@kw, ContractRequires, KwContractRequires) }; (Crate) => { exp!(@kw, Crate, KwCrate) }; (Default) => { exp!(@kw, Default, KwDefault) }; (Dyn) => { exp!(@kw, Dyn, KwDyn) }; @@ -550,8 +552,6 @@ macro_rules! exp { (Ref) => { exp!(@kw, Ref, KwRef) }; (Return) => { exp!(@kw, Return, KwReturn) }; (Reuse) => { exp!(@kw, Reuse, KwReuse) }; - (RustcContractEnsures) => { exp!(@kw, RustcContractEnsures, KwRustcContractEnsures) }; - (RustcContractRequires) => { exp!(@kw, RustcContractRequires, KwRustcContractRequires) }; (Safe) => { exp!(@kw, Safe, KwSafe) }; (SelfUpper) => { exp!(@kw, SelfUpper, KwSelfUpper) }; (Static) => { exp!(@kw, Static, KwStatic) }; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ea2ce5475c264..529dfc6ff7a03 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -118,8 +118,8 @@ symbols! { MacroRules: "macro_rules", Raw: "raw", Reuse: "reuse", - RustcContractEnsures: "rustc_contract_ensures", - RustcContractRequires: "rustc_contract_requires", + ContractEnsures: "contract_ensures", + ContractRequires: "contract_requires", Safe: "safe", Union: "union", Yeet: "yeet", @@ -682,7 +682,9 @@ symbols! { contract_check_ensures, contract_check_requires, contract_checks, + contracts, contracts_ensures, + contracts_internals, contracts_requires, convert_identity, copy, @@ -1716,8 +1718,6 @@ symbols! { rustc_const_stable, rustc_const_stable_indirect, rustc_const_unstable, - rustc_contracts, - rustc_contracts_internals, rustc_conversion_suggestion, rustc_deallocator, rustc_def_path, diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs index 0668cacb92c60..c769e219e4d49 100644 --- a/library/core/src/contracts.rs +++ b/library/core/src/contracts.rs @@ -6,7 +6,7 @@ pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_require /// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }` /// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }` /// (including the implicit return of the tail expression, if any). -#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] #[lang = "contract_build_check_ensures"] #[track_caller] pub fn build_check_ensures(cond: C) -> impl (Fn(Ret) -> Ret) + Copy diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 14f8645d6f1e5..1e4dc12f9b617 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -4051,8 +4051,8 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) /// 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_internals", issue = "133866" /* compiler-team#759 */)] -#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] +#[rustc_const_unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] #[inline(always)] #[rustc_intrinsic] pub const fn contract_checks() -> bool { @@ -4067,7 +4067,7 @@ pub const fn contract_checks() -> bool { /// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition /// returns false. #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] #[lang = "contract_check_requires"] #[rustc_intrinsic] pub fn contract_check_requires bool>(cond: C) { @@ -4082,7 +4082,7 @@ pub fn contract_check_requires bool>(cond: C) { /// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition /// returns false. #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] #[rustc_intrinsic] pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) { if contract_checks() && !cond(ret) { diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 6a0051244f062..de8e85f7b36ed 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -248,7 +248,7 @@ pub mod autodiff { } #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts", issue = "133866")] +#[unstable(feature = "contracts", issue = "128044")] pub mod contracts; #[unstable(feature = "cfg_match", issue = "115585")] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index cb37530e90db8..4c6fd196bd31c 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1783,8 +1783,8 @@ pub(crate) mod builtin { /// eventually parsed as a unary closure expression that is /// invoked on a reference to the return value. #[cfg(not(bootstrap))] - #[unstable(feature = "rustc_contracts", issue = "133866")] - #[allow_internal_unstable(rustc_contracts_internals)] + #[unstable(feature = "contracts", issue = "128044")] + #[allow_internal_unstable(contracts_internals)] #[rustc_builtin_macro] pub macro contracts_ensures($item:item) { /* compiler built-in */ @@ -1796,8 +1796,8 @@ pub(crate) mod builtin { /// eventually parsed as an boolean expression with access to the /// function's formal parameters #[cfg(not(bootstrap))] - #[unstable(feature = "rustc_contracts", issue = "133866")] - #[allow_internal_unstable(rustc_contracts_internals)] + #[unstable(feature = "contracts", issue = "128044")] + #[allow_internal_unstable(contracts_internals)] #[rustc_builtin_macro] pub macro contracts_requires($item:item) { /* compiler built-in */ diff --git a/tests/ui/contracts/contract-annotation-limitations.rs b/tests/ui/contracts/contract-annotation-limitations.rs index f01d526e3f70e..10b3bacab5cfa 100644 --- a/tests/ui/contracts/contract-annotation-limitations.rs +++ b/tests/ui/contracts/contract-annotation-limitations.rs @@ -1,7 +1,8 @@ //! Test for some of the existing limitations and the current error messages. //! Some of these limitations may be removed in the future. -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #![allow(dead_code)] /// Represent a 5-star system. diff --git a/tests/ui/contracts/contract-annotation-limitations.stderr b/tests/ui/contracts/contract-annotation-limitations.stderr index 25b01744aac8e..14338cf4b8687 100644 --- a/tests/ui/contracts/contract-annotation-limitations.stderr +++ b/tests/ui/contracts/contract-annotation-limitations.stderr @@ -1,14 +1,23 @@ error: contract annotations is only supported in functions with bodies - --> $DIR/contract-annotation-limitations.rs:17:5 + --> $DIR/contract-annotation-limitations.rs:18:5 | LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations is only supported in functions with bodies - --> $DIR/contract-annotation-limitations.rs:21:5 + --> $DIR/contract-annotation-limitations.rs:22:5 | LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-annotation-limitations.rs:4:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr b/tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr new file mode 100644 index 0000000000000..0630811d4f7ea --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr b/tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr new file mode 100644 index 0000000000000..0630811d4f7ea --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr new file mode 100644 index 0000000000000..0630811d4f7ea --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_pass.stderr b/tests/ui/contracts/contract-attributes-generics.chk_pass.stderr new file mode 100644 index 0000000000000..0630811d4f7ea --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.rs b/tests/ui/contracts/contract-attributes-generics.rs index 87088ce9de2ce..fd79c6abedd85 100644 --- a/tests/ui/contracts/contract-attributes-generics.rs +++ b/tests/ui/contracts/contract-attributes-generics.rs @@ -16,7 +16,8 @@ //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes //@ [chk_const_fail] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] use std::ops::Sub; diff --git a/tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr b/tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr new file mode 100644 index 0000000000000..0630811d4f7ea --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr b/tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr new file mode 100644 index 0000000000000..9ca95b8bb01a4 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr new file mode 100644 index 0000000000000..9ca95b8bb01a4 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.chk_pass.stderr b/tests/ui/contracts/contract-attributes-nest.chk_pass.stderr new file mode 100644 index 0000000000000..9ca95b8bb01a4 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.rs b/tests/ui/contracts/contract-attributes-nest.rs index 1cda21f10d7ec..e1e61b88f282e 100644 --- a/tests/ui/contracts/contract-attributes-nest.rs +++ b/tests/ui/contracts/contract-attributes-nest.rs @@ -16,7 +16,8 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #[core::contracts::requires(x.baz > 0)] #[core::contracts::ensures(|ret| *ret > 100)] diff --git a/tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr b/tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr new file mode 100644 index 0000000000000..9ca95b8bb01a4 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr new file mode 100644 index 0000000000000..9ca95b8bb01a4 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr b/tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr new file mode 100644 index 0000000000000..9ca95b8bb01a4 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr b/tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr new file mode 100644 index 0000000000000..f87e7e19fa3db --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr new file mode 100644 index 0000000000000..f87e7e19fa3db --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.chk_pass.stderr b/tests/ui/contracts/contract-attributes-tail.chk_pass.stderr new file mode 100644 index 0000000000000..f87e7e19fa3db --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.rs b/tests/ui/contracts/contract-attributes-tail.rs index 26855bfa82ac4..ce4a6be5b82f7 100644 --- a/tests/ui/contracts/contract-attributes-tail.rs +++ b/tests/ui/contracts/contract-attributes-tail.rs @@ -16,7 +16,8 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #[core::contracts::requires(x.baz > 0)] #[core::contracts::ensures(|ret| *ret > 100)] diff --git a/tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr b/tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr new file mode 100644 index 0000000000000..f87e7e19fa3db --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr new file mode 100644 index 0000000000000..f87e7e19fa3db --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr b/tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr new file mode 100644 index 0000000000000..f87e7e19fa3db --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-captures-via-closure-copy.rs b/tests/ui/contracts/contract-captures-via-closure-copy.rs index 742895ab0ad80..32c6d2bf4fe17 100644 --- a/tests/ui/contracts/contract-captures-via-closure-copy.rs +++ b/tests/ui/contracts/contract-captures-via-closure-copy.rs @@ -1,7 +1,8 @@ //@ run-fail //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] struct Baz { baz: i32 diff --git a/tests/ui/contracts/contract-captures-via-closure-copy.stderr b/tests/ui/contracts/contract-captures-via-closure-copy.stderr new file mode 100644 index 0000000000000..d92db601608f5 --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-copy.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-captures-via-closure-copy.rs:4:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-captures-via-closure-noncopy.rs b/tests/ui/contracts/contract-captures-via-closure-noncopy.rs index 8d7f2fd200e2c..976f64c7fd911 100644 --- a/tests/ui/contracts/contract-captures-via-closure-noncopy.rs +++ b/tests/ui/contracts/contract-captures-via-closure-noncopy.rs @@ -1,6 +1,7 @@ //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] struct Baz { baz: i32 diff --git a/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr index b53809827f987..4a47671fee191 100644 --- a/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr +++ b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr @@ -1,16 +1,25 @@ -error[E0277]: the trait bound `Baz: std::marker::Copy` is not satisfied in `{closure@$DIR/contract-captures-via-closure-noncopy.rs:11:42: 11:57}` - --> $DIR/contract-captures-via-closure-noncopy.rs:11:1 +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-captures-via-closure-noncopy.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the trait bound `Baz: std::marker::Copy` is not satisfied in `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}` + --> $DIR/contract-captures-via-closure-noncopy.rs:12:1 | LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------------------------------^^^^ | | | - | | within this `{closure@$DIR/contract-captures-via-closure-noncopy.rs:11:42: 11:57}` - | | this tail expression is of type `{closure@contract-captures-via-closure-noncopy.rs:11:42}` + | | within this `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}` + | | this tail expression is of type `{closure@contract-captures-via-closure-noncopy.rs:12:42}` | unsatisfied trait bound | - = help: within `{closure@$DIR/contract-captures-via-closure-noncopy.rs:11:42: 11:57}`, the trait `std::marker::Copy` is not implemented for `Baz` + = help: within `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}`, the trait `std::marker::Copy` is not implemented for `Baz` note: required because it's used within this closure - --> $DIR/contract-captures-via-closure-noncopy.rs:11:42 + --> $DIR/contract-captures-via-closure-noncopy.rs:12:42 | LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] | ^^^^^^^^^^^^^^^ @@ -22,6 +31,6 @@ LL + #[derive(Copy)] LL | struct Baz { | -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr new file mode 100644 index 0000000000000..d693fad446a4d --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr new file mode 100644 index 0000000000000..d693fad446a4d --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr new file mode 100644 index 0000000000000..d693fad446a4d --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr new file mode 100644 index 0000000000000..d693fad446a4d --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.rs b/tests/ui/contracts/contracts-ensures-early-fn-exit.rs index faf97473a90f2..034cead3b4e9f 100644 --- a/tests/ui/contracts/contracts-ensures-early-fn-exit.rs +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.rs @@ -13,7 +13,8 @@ //@ [chk_fail_yeet] compile-flags: -Zcontract-checks=yes //! This test ensures that ensures clauses are checked for different return points of a function. -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #![feature(yeet_expr)] /// This ensures will fail in different return points depending on the input. diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr new file mode 100644 index 0000000000000..d693fad446a4d --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs index 9872fdb1a1852..f01a852fbff34 100644 --- a/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs @@ -1,6 +1,7 @@ //@ run-pass //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #[core::contracts::ensures(|ret| *ret > 0)] fn outer() -> i32 { diff --git a/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr new file mode 100644 index 0000000000000..49a372b53c7d8 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-is-not-inherited-when-nesting.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs index 75124259b0d72..2c2a4a6978550 100644 --- a/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs @@ -1,6 +1,7 @@ //@ run-pass //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] struct Outer { outer: std::cell::Cell } diff --git a/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr new file mode 100644 index 0000000000000..48898c4434ad5 --- /dev/null +++ b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-requires-is-not-inherited-when-nesting.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs index 76ed30e856462..69be906782a67 100644 --- a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs +++ b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs @@ -1,6 +1,7 @@ //! Checks for compilation errors related to adding contracts to non-function items. -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #![allow(dead_code)] #[core::contracts::requires(true)] diff --git a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr index 4d6d23340ac0e..0a7fff8183e09 100644 --- a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr +++ b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr @@ -1,44 +1,53 @@ error: contract annotations can only be used on functions - --> $DIR/disallow-contract-annotation-on-non-fn.rs:6:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:7:1 | LL | #[core::contracts::requires(true)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations can only be used on functions - --> $DIR/disallow-contract-annotation-on-non-fn.rs:10:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:11:1 | LL | #[core::contracts::ensures(|v| v == 100)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations is only supported in functions with bodies - --> $DIR/disallow-contract-annotation-on-non-fn.rs:15:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:16:1 | LL | #[core::contracts::ensures(|v| v == 100)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations is only supported in functions with bodies - --> $DIR/disallow-contract-annotation-on-non-fn.rs:19:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:20:1 | LL | #[core::contracts::ensures(|v| v == 100)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations can only be used on functions - --> $DIR/disallow-contract-annotation-on-non-fn.rs:23:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:24:1 | LL | #[core::contracts::requires(true)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations can only be used on functions - --> $DIR/disallow-contract-annotation-on-non-fn.rs:34:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:35:1 | LL | #[core::contracts::ensures(|dummy| dummy.0 > 0)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations can only be used on functions - --> $DIR/disallow-contract-annotation-on-non-fn.rs:45:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:46:1 | LL | #[core::contracts::requires(true)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/disallow-contract-annotation-on-non-fn.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to 7 previous errors; 1 warning emitted diff --git a/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs index d95ccd4f7b9b6..6d8cd3949eedb 100644 --- a/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs @@ -16,11 +16,11 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts_internals)] +#![feature(contracts_internals)] fn nest(x: Baz) -> i32 - rustc_contract_requires(|| x.baz > 0) - rustc_contract_ensures(|ret| *ret > 100) + contract_requires(|| x.baz > 0) + contract_ensures(|ret| *ret > 100) { loop { return x.baz + 50; diff --git a/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs index 636a595e06adb..07ec26f921b80 100644 --- a/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs @@ -16,11 +16,11 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts_internals)] +#![feature(contracts_internals)] fn tail(x: Baz) -> i32 - rustc_contract_requires(|| x.baz > 0) - rustc_contract_ensures(|ret| *ret > 100) + contract_requires(|| x.baz > 0) + contract_ensures(|ret| *ret > 100) { x.baz + 50 } diff --git a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs index 8c70c1a85f6d3..ae692afd146fe 100644 --- a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs +++ b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs @@ -10,7 +10,7 @@ //@ [chk_pass] compile-flags: -Zcontract-checks=yes //@ [chk_fail_requires] compile-flags: -Zcontract-checks=yes //@ [chk_fail_ensures] compile-flags: -Zcontract-checks=yes -#![feature(cfg_contract_checks, rustc_contracts_internals, core_intrinsics)] +#![feature(cfg_contract_checks, contracts_internals, core_intrinsics)] fn main() { #[cfg(any(default, unchk_pass))] // default: disabled diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr new file mode 100644 index 0000000000000..a60ce1602659b --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr new file mode 100644 index 0000000000000..a60ce1602659b --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.rs b/tests/ui/contracts/internal_machinery/contract-lang-items.rs index ff569e011f206..e91bbed294d12 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.rs +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.rs @@ -12,8 +12,9 @@ //@ [chk_pass] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] // to access core::contracts -#![feature(rustc_contracts_internals)] // to access check_requires lang item +#![feature(contracts)] // to access core::contracts +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] +#![feature(contracts_internals)] // to access check_requires lang item fn foo(x: Baz) -> i32 { let injected_checker = { diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr new file mode 100644 index 0000000000000..a60ce1602659b --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr new file mode 100644 index 0000000000000..a60ce1602659b --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs index 0b0151c6df742..960ccaed3588a 100644 --- a/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs @@ -1,9 +1,9 @@ //@ run-pass //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts_internals)] +#![feature(contracts_internals)] fn outer() -> i32 - rustc_contract_ensures(|ret| *ret > 0) + contract_ensures(|ret| *ret > 0) { let inner_closure = || -> i32 { 0 }; inner_closure(); diff --git a/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs index 79c50a18f7056..bee703de16a0b 100644 --- a/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs @@ -1,11 +1,11 @@ //@ run-pass //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts_internals)] +#![feature(contracts_internals)] struct Outer { outer: std::cell::Cell } fn outer(x: Outer) - rustc_contract_requires(|| x.outer.get() > 0) + contract_requires(|| x.outer.get() > 0) { let inner_closure = || { }; x.outer.set(0); diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs index ee9edf3ebb0c2..1b76eef6780fe 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs @@ -1,20 +1,20 @@ -// gate-test-rustc_contracts_internals +// gate-test-contracts_internals fn main() { - // intrinsics are guarded by rustc_contracts_internals feature gate. + // intrinsics are guarded by contracts_internals feature gate. core::intrinsics::contract_checks(); - //~^ ERROR use of unstable library feature `rustc_contracts_internals` + //~^ ERROR use of unstable library feature `contracts_internals` core::intrinsics::contract_check_requires(|| true); - //~^ ERROR use of unstable library feature `rustc_contracts_internals` + //~^ ERROR use of unstable library feature `contracts_internals` core::intrinsics::contract_check_ensures(&1, |_|true); - //~^ ERROR use of unstable library feature `rustc_contracts_internals` + //~^ ERROR use of unstable library feature `contracts_internals` core::contracts::build_check_ensures(|_: &()| true); - //~^ ERROR use of unstable library feature `rustc_contracts_internals` + //~^ ERROR use of unstable library feature `contracts_internals` - // ast extensions are guarded by rustc_contracts_internals feature gate - fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 } + // ast extensions are guarded by contracts_internals feature gate + fn identity_1() -> i32 contract_requires(|| true) { 10 } //~^ ERROR contract internal machinery is for internal use only - fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 } + fn identity_2() -> i32 contract_ensures(|_| true) { 10 } //~^ ERROR contract internal machinery is for internal use only } diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr index 5f9263e03e85a..c0e1522f54c61 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr @@ -1,61 +1,61 @@ error[E0658]: contract internal machinery is for internal use only - --> $DIR/internal-feature-gating.rs:16:51 + --> $DIR/internal-feature-gating.rs:16:45 | -LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 } - | ^^^^^^^^^ +LL | fn identity_1() -> i32 contract_requires(|| true) { 10 } + | ^^^^^^^^^ | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` 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[E0658]: contract internal machinery is for internal use only - --> $DIR/internal-feature-gating.rs:18:50 + --> $DIR/internal-feature-gating.rs:18:44 | -LL | fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 } - | ^^^^^^^^^^ +LL | fn identity_2() -> i32 contract_ensures(|_| true) { 10 } + | ^^^^^^^^^^ | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` 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[E0658]: use of unstable library feature `rustc_contracts_internals` +error[E0658]: use of unstable library feature `contracts_internals` --> $DIR/internal-feature-gating.rs:5:5 | LL | core::intrinsics::contract_checks(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` 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[E0658]: use of unstable library feature `rustc_contracts_internals` +error[E0658]: use of unstable library feature `contracts_internals` --> $DIR/internal-feature-gating.rs:7:5 | LL | core::intrinsics::contract_check_requires(|| true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` 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[E0658]: use of unstable library feature `rustc_contracts_internals` +error[E0658]: use of unstable library feature `contracts_internals` --> $DIR/internal-feature-gating.rs:9:5 | LL | core::intrinsics::contract_check_ensures(&1, |_|true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` 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[E0658]: use of unstable library feature `rustc_contracts_internals` +error[E0658]: use of unstable library feature `contracts_internals` --> $DIR/internal-feature-gating.rs:12:5 | LL | core::contracts::build_check_ensures(|_: &()| true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` 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 6 previous errors diff --git a/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr index af4e605e57098..89c6d077f97eb 100644 --- a/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr +++ b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr @@ -4,7 +4,7 @@ error[E0658]: `cfg(contract_checks)` is experimental and subject to change LL | cfg!(contract_checks) | ^^^^^^^^^^^^^^^ | - = note: see issue #133866 for more information + = note: see issue #128044 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 diff --git a/tests/ui/feature-gates/feature-gate-contracts.rs b/tests/ui/feature-gates/feature-gate-contracts.rs new file mode 100644 index 0000000000000..5544f1d82ee4c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-contracts.rs @@ -0,0 +1,11 @@ +#![crate_type = "lib"] + +#[core::contracts::requires(x > 0)] +pub fn requires_needs_it(x: i32) { } +//~^^ ERROR use of unstable library feature `contracts` +//~^^^ ERROR contracts are incomplete + +#[core::contracts::ensures(|ret| *ret > 0)] +pub fn ensures_needs_it() -> i32 { 10 } +//~^^ ERROR use of unstable library feature `contracts` +//~^^^ ERROR contracts are incomplete diff --git a/tests/ui/feature-gates/feature-gate-contracts.stderr b/tests/ui/feature-gates/feature-gate-contracts.stderr new file mode 100644 index 0000000000000..4403e7df50b49 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-contracts.stderr @@ -0,0 +1,43 @@ +error[E0658]: use of unstable library feature `contracts` + --> $DIR/feature-gate-contracts.rs:3:3 + | +LL | #[core::contracts::requires(x > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` 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[E0658]: use of unstable library feature `contracts` + --> $DIR/feature-gate-contracts.rs:8:3 + | +LL | #[core::contracts::ensures(|ret| *ret > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` 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[E0658]: contracts are incomplete + --> $DIR/feature-gate-contracts.rs:3:1 + | +LL | #[core::contracts::requires(x > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` 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[E0658]: contracts are incomplete + --> $DIR/feature-gate-contracts.rs:8:1 + | +LL | #[core::contracts::ensures(|ret| *ret > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` 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 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-rustc-contracts.rs b/tests/ui/feature-gates/feature-gate-rustc-contracts.rs deleted file mode 100644 index d4249c252cda3..0000000000000 --- a/tests/ui/feature-gates/feature-gate-rustc-contracts.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![crate_type = "lib"] - -#[core::contracts::requires(x > 0)] -pub fn requires_needs_it(x: i32) { } -//~^^ ERROR use of unstable library feature `rustc_contracts` -//~^^^ ERROR contracts are experimental - -#[core::contracts::ensures(|ret| *ret > 0)] -pub fn ensures_needs_it() -> i32 { 10 } -//~^^ ERROR use of unstable library feature `rustc_contracts` -//~^^^ ERROR contracts are experimental diff --git a/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr b/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr deleted file mode 100644 index eb7777a4a5179..0000000000000 --- a/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr +++ /dev/null @@ -1,43 +0,0 @@ -error[E0658]: use of unstable library feature `rustc_contracts` - --> $DIR/feature-gate-rustc-contracts.rs:3:3 - | -LL | #[core::contracts::requires(x > 0)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts)]` 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[E0658]: use of unstable library feature `rustc_contracts` - --> $DIR/feature-gate-rustc-contracts.rs:8:3 - | -LL | #[core::contracts::ensures(|ret| *ret > 0)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts)]` 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[E0658]: contracts are experimental - --> $DIR/feature-gate-rustc-contracts.rs:3:1 - | -LL | #[core::contracts::requires(x > 0)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts)]` 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[E0658]: contracts are experimental - --> $DIR/feature-gate-rustc-contracts.rs:8:1 - | -LL | #[core::contracts::ensures(|ret| *ret > 0)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts)]` 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 4 previous errors - -For more information about this error, try `rustc --explain E0658`. From 8b1c28fdd0716e16a8d1776b40d2896128dd7654 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 3 Feb 2025 19:17:46 -0500 Subject: [PATCH 25/28] Fix ICE when function argument mismatches. --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 5 +- tests/crashes/135124.rs | 9 ---- tests/ui/fn/error-recovery-mismatch.rs | 20 ++++++++ tests/ui/fn/error-recovery-mismatch.stderr | 46 +++++++++++++++++++ 4 files changed, 70 insertions(+), 10 deletions(-) delete mode 100644 tests/crashes/135124.rs create mode 100644 tests/ui/fn/error-recovery-mismatch.rs create mode 100644 tests/ui/fn/error-recovery-mismatch.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index e30c0e115dc2f..e90474cabb420 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -126,7 +126,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(guar) => Err(guar), }; if let Err(guar) = has_error { - let err_inputs = self.err_args(args_no_rcvr.len(), guar); + let err_inputs = self.err_args( + method.map_or(args_no_rcvr.len(), |method| method.sig.inputs().len() - 1), + guar, + ); let err_output = Ty::new_error(self.tcx, guar); let err_inputs = match tuple_arguments { diff --git a/tests/crashes/135124.rs b/tests/crashes/135124.rs deleted file mode 100644 index d6655cb46fac2..0000000000000 --- a/tests/crashes/135124.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ known-bug: #135124 -trait A { - fn y(&self) - { - fn call() -> impl Sized {} - self.fold(call()); - } - fn fold(&self, &self._) {} -} diff --git a/tests/ui/fn/error-recovery-mismatch.rs b/tests/ui/fn/error-recovery-mismatch.rs new file mode 100644 index 0000000000000..a50a30c8c78a6 --- /dev/null +++ b/tests/ui/fn/error-recovery-mismatch.rs @@ -0,0 +1,20 @@ +// Used to ICE due to a size mismatch between the actual fake signature of `fold` and the +// generated signature used reporting the parameter mismatch at the call site. +// See issue #135124 + +trait A { + fn y(&self) + { + fn call() -> impl Sized {} + self.fold(call(), call()); + } + fn fold(&self, _: T, &self._) {} + //~^ ERROR unexpected `self` parameter in function + //~| ERROR expected one of `)` or `,`, found `.` + //~| ERROR identifier `self` is bound more than once in this parameter list + //~| WARNING anonymous parameters are deprecated + //~| WARNING this is accepted in the current edition + //~| ERROR the placeholder `_` is not allowed within types +} + +fn main() {} diff --git a/tests/ui/fn/error-recovery-mismatch.stderr b/tests/ui/fn/error-recovery-mismatch.stderr new file mode 100644 index 0000000000000..ad4652c11c12f --- /dev/null +++ b/tests/ui/fn/error-recovery-mismatch.stderr @@ -0,0 +1,46 @@ +error: unexpected `self` parameter in function + --> $DIR/error-recovery-mismatch.rs:11:29 + | +LL | fn fold(&self, _: T, &self._) {} + | ^^^^^ must be the first parameter of an associated function + +error: expected one of `)` or `,`, found `.` + --> $DIR/error-recovery-mismatch.rs:11:34 + | +LL | fn fold(&self, _: T, &self._) {} + | ^ + | | + | expected one of `)` or `,` + | help: missing `,` + +error[E0415]: identifier `self` is bound more than once in this parameter list + --> $DIR/error-recovery-mismatch.rs:11:30 + | +LL | fn fold(&self, _: T, &self._) {} + | ^^^^ used as parameter more than once + +warning: anonymous parameters are deprecated and will be removed in the next edition + --> $DIR/error-recovery-mismatch.rs:11:35 + | +LL | fn fold(&self, _: T, &self._) {} + | ^ help: try naming the parameter or explicitly ignoring it: `_: _` + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! + = note: for more information, see issue #41686 + = note: `#[warn(anonymous_parameters)]` on by default + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/error-recovery-mismatch.rs:11:35 + | +LL | fn fold(&self, _: T, &self._) {} + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn fold(&self, _: T, &self.U) {} + | +++ ~ + +error: aborting due to 4 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0121, E0415. +For more information about an error, try `rustc --explain E0121`. From c4888de68eec3f392c966c492a779400374bb89f Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 4 Feb 2025 15:59:58 +1100 Subject: [PATCH 26/28] Remove helper trait `UserAnnotatedTyHelpers` This trait doesn't appear to provide any benefit over a simple helper function. --- compiler/rustc_mir_build/src/thir/cx/expr.rs | 80 +++++++++---------- compiler/rustc_mir_build/src/thir/cx/mod.rs | 16 ++-- .../rustc_mir_build/src/thir/pattern/mod.rs | 34 +++----- compiler/rustc_mir_build/src/thir/util.rs | 44 +++++----- 4 files changed, 74 insertions(+), 100 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 795ac6b4bea54..8eec0fe12d414 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -22,7 +22,6 @@ use rustc_span::{Span, sym}; use tracing::{debug, info, instrument, trace}; use crate::thir::cx::Cx; -use crate::thir::util::UserAnnotatedTyHelpers; impl<'tcx> Cx<'tcx> { /// Create a THIR expression for the given HIR expression. This expands all @@ -142,9 +141,9 @@ impl<'tcx> Cx<'tcx> { Adjust::Deref(Some(deref)) => { // We don't need to do call adjust_span here since // deref coercions always start with a built-in deref. - let call_def_id = deref.method_call(self.tcx()); + let call_def_id = deref.method_call(self.tcx); let overloaded_callee = - Ty::new_fn_def(self.tcx(), call_def_id, self.tcx().mk_args(&[expr.ty.into()])); + Ty::new_fn_def(self.tcx, call_def_id, self.tcx.mk_args(&[expr.ty.into()])); expr = Expr { temp_lifetime, @@ -253,10 +252,10 @@ impl<'tcx> Cx<'tcx> { // Check to see if this cast is a "coercion cast", where the cast is actually done // using a coercion (or is a no-op). - if self.typeck_results().is_coercion_cast(source.hir_id) { + if self.typeck_results.is_coercion_cast(source.hir_id) { // Convert the lexpr to a vexpr. ExprKind::Use { source: self.mirror_expr(source) } - } else if self.typeck_results().expr_ty(source).is_ref() { + } else if self.typeck_results.expr_ty(source).is_ref() { // Special cased so that we can type check that the element // type of the source matches the pointed to type of the // destination. @@ -266,8 +265,8 @@ impl<'tcx> Cx<'tcx> { is_from_as_cast: true, } } else if let hir::ExprKind::Path(ref qpath) = source.kind - && let res = self.typeck_results().qpath_res(qpath, source.hir_id) - && let ty = self.typeck_results().node_type(source.hir_id) + && let res = self.typeck_results.qpath_res(qpath, source.hir_id) + && let ty = self.typeck_results.node_type(source.hir_id) && let ty::Adt(adt_def, args) = ty.kind() && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res { @@ -330,7 +329,7 @@ impl<'tcx> Cx<'tcx> { #[instrument(level = "debug", skip(self), ret)] fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> { let tcx = self.tcx; - let expr_ty = self.typeck_results().expr_ty(expr); + let expr_ty = self.typeck_results.expr_ty(expr); let (temp_lifetime, backwards_incompatible) = self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id); @@ -354,7 +353,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Call(fun, ref args) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { // The callee is something implementing Fn, FnMut, or FnOnce. // Find the actual method implementation being called and // build the appropriate UFCS call expression with the @@ -364,7 +363,7 @@ impl<'tcx> Cx<'tcx> { let method = self.method_callee(expr, fun.span, None); - let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e)); + let arg_tys = args.iter().map(|e| self.typeck_results.expr_ty_adjusted(e)); let tupled_args = Expr { ty: Ty::new_tup_from_iter(tcx, arg_tys), temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, @@ -380,7 +379,7 @@ impl<'tcx> Cx<'tcx> { from_hir_call: true, fn_span: expr.span, } - } else if let ty::FnDef(def_id, _) = self.typeck_results().expr_ty(fun).kind() + } else if let ty::FnDef(def_id, _) = self.typeck_results.expr_ty(fun).kind() && let Some(intrinsic) = self.tcx.intrinsic(def_id) && intrinsic.name == sym::box_new { @@ -413,7 +412,7 @@ impl<'tcx> Cx<'tcx> { }, hir::QPath::TypeRelative(_ty, _) => { if let Some((DefKind::Ctor(_, CtorKind::Fn), ctor_id)) = - self.typeck_results().type_dependent_def(fun.hir_id) + self.typeck_results.type_dependent_def(fun.hir_id) { Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id))) } else { @@ -426,8 +425,8 @@ impl<'tcx> Cx<'tcx> { None }; if let Some((adt_def, index)) = adt_data { - let node_args = self.typeck_results().node_args(fun.hir_id); - let user_provided_types = self.typeck_results().user_provided_types(); + let node_args = self.typeck_results.node_args(fun.hir_id); + let user_provided_types = self.typeck_results.user_provided_types(); let user_ty = user_provided_types.get(fun.hir_id).copied().map(|mut u_ty| { if let ty::UserTypeKind::TypeOf(ref mut did, _) = @@ -457,7 +456,7 @@ impl<'tcx> Cx<'tcx> { })) } else { ExprKind::Call { - ty: self.typeck_results().node_type(fun.hir_id), + ty: self.typeck_results.node_type(fun.hir_id), fun: self.mirror_expr(fun), args: self.mirror_exprs(args), from_hir_call: true, @@ -482,7 +481,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::AssignOp(op, lhs, rhs) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { let lhs = self.mirror_expr(lhs); let rhs = self.mirror_expr(rhs); self.overloaded_operator(expr, Box::new([lhs, rhs])) @@ -498,7 +497,7 @@ impl<'tcx> Cx<'tcx> { hir::ExprKind::Lit(lit) => ExprKind::Literal { lit, neg: false }, hir::ExprKind::Binary(op, lhs, rhs) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { let lhs = self.mirror_expr(lhs); let rhs = self.mirror_expr(rhs); self.overloaded_operator(expr, Box::new([lhs, rhs])) @@ -527,7 +526,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Index(lhs, index, brackets_span) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { let lhs = self.mirror_expr(lhs); let index = self.mirror_expr(index); self.overloaded_place( @@ -543,7 +542,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Unary(hir::UnOp::Deref, arg) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { let arg = self.mirror_expr(arg); self.overloaded_place(expr, expr_ty, None, Box::new([arg]), expr.span) } else { @@ -552,7 +551,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Unary(hir::UnOp::Not, arg) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { let arg = self.mirror_expr(arg); self.overloaded_operator(expr, Box::new([arg])) } else { @@ -561,7 +560,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Unary(hir::UnOp::Neg, arg) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { let arg = self.mirror_expr(arg); self.overloaded_operator(expr, Box::new([arg])) } else if let hir::ExprKind::Lit(lit) = arg.kind { @@ -574,7 +573,7 @@ impl<'tcx> Cx<'tcx> { hir::ExprKind::Struct(qpath, fields, ref base) => match expr_ty.kind() { ty::Adt(adt, args) => match adt.adt_kind() { AdtKind::Struct | AdtKind::Union => { - let user_provided_types = self.typeck_results().user_provided_types(); + let user_provided_types = self.typeck_results.user_provided_types(); let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new); debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty); ExprKind::Adt(Box::new(AdtExpr { @@ -586,15 +585,14 @@ impl<'tcx> Cx<'tcx> { base: match base { hir::StructTailExpr::Base(base) => AdtExprBase::Base(FruInfo { base: self.mirror_expr(base), - field_types: self.typeck_results().fru_field_types() - [expr.hir_id] + field_types: self.typeck_results.fru_field_types()[expr.hir_id] .iter() .copied() .collect(), }), hir::StructTailExpr::DefaultFields(_) => { AdtExprBase::DefaultFields( - self.typeck_results().fru_field_types()[expr.hir_id] + self.typeck_results.fru_field_types()[expr.hir_id] .iter() .copied() .collect(), @@ -605,7 +603,7 @@ impl<'tcx> Cx<'tcx> { })) } AdtKind::Enum => { - let res = self.typeck_results().qpath_res(qpath, expr.hir_id); + let res = self.typeck_results.qpath_res(qpath, expr.hir_id); match res { Res::Def(DefKind::Variant, variant_id) => { assert!(matches!( @@ -615,8 +613,7 @@ impl<'tcx> Cx<'tcx> { )); let index = adt.variant_index_with_id(variant_id); - let user_provided_types = - self.typeck_results().user_provided_types(); + let user_provided_types = self.typeck_results.user_provided_types(); let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new); debug!("make_mirror_unadjusted: (variant) user_ty={:?}", user_ty); @@ -629,8 +626,7 @@ impl<'tcx> Cx<'tcx> { base: match base { hir::StructTailExpr::DefaultFields(_) => { AdtExprBase::DefaultFields( - self.typeck_results().fru_field_types() - [expr.hir_id] + self.typeck_results.fru_field_types()[expr.hir_id] .iter() .copied() .collect(), @@ -655,7 +651,7 @@ impl<'tcx> Cx<'tcx> { }, hir::ExprKind::Closure { .. } => { - let closure_ty = self.typeck_results().expr_ty(expr); + let closure_ty = self.typeck_results.expr_ty(expr); let (def_id, args, movability) = match *closure_ty.kind() { ty::Closure(def_id, args) => (def_id, UpvarArgs::Closure(args), None), ty::Coroutine(def_id, args) => { @@ -703,7 +699,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Path(ref qpath) => { - let res = self.typeck_results().qpath_res(qpath, expr.hir_id); + let res = self.typeck_results.qpath_res(qpath, expr.hir_id); self.convert_path_expr(expr, res) } @@ -772,7 +768,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::ConstBlock(ref anon_const) => { - let ty = self.typeck_results().node_type(anon_const.hir_id); + let ty = self.typeck_results.node_type(anon_const.hir_id); let did = anon_const.def_id.to_def_id(); let typeck_root_def_id = tcx.typeck_root_def_id(did); let parent_args = @@ -783,7 +779,7 @@ impl<'tcx> Cx<'tcx> { } // Now comes the rote stuff: hir::ExprKind::Repeat(v, _) => { - let ty = self.typeck_results().expr_ty(expr); + let ty = self.typeck_results.expr_ty(expr); let ty::Array(_, count) = ty.kind() else { span_bug!(expr.span, "unexpected repeat expr ty: {:?}", ty); }; @@ -837,7 +833,7 @@ impl<'tcx> Cx<'tcx> { match_source, }, hir::ExprKind::Loop(body, ..) => { - let block_ty = self.typeck_results().node_type(body.hir_id); + let block_ty = self.typeck_results.node_type(body.hir_id); let (temp_lifetime, backwards_incompatible) = self .rvalue_scopes .temporary_scope(self.region_scope_tree, body.hir_id.local_id); @@ -957,7 +953,7 @@ impl<'tcx> Cx<'tcx> { | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { - self.typeck_results().user_provided_types().get(hir_id).copied().map(Box::new) + self.typeck_results.user_provided_types().get(hir_id).copied().map(Box::new) } // A unit struct/variant which is used as a value (e.g., @@ -989,17 +985,13 @@ impl<'tcx> Cx<'tcx> { Some(fn_def) => (fn_def, None), None => { let (kind, def_id) = - self.typeck_results().type_dependent_def(expr.hir_id).unwrap_or_else(|| { + self.typeck_results.type_dependent_def(expr.hir_id).unwrap_or_else(|| { span_bug!(expr.span, "no type-dependent def for method callee") }); let user_ty = self.user_args_applied_to_res(expr.hir_id, Res::Def(kind, def_id)); debug!("method_callee: user_ty={:?}", user_ty); ( - Ty::new_fn_def( - self.tcx(), - def_id, - self.typeck_results().node_args(expr.hir_id), - ), + Ty::new_fn_def(self.tcx, def_id, self.typeck_results.node_args(expr.hir_id)), user_ty, ) } @@ -1025,7 +1017,7 @@ impl<'tcx> Cx<'tcx> { } fn convert_path_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, res: Res) -> ExprKind<'tcx> { - let args = self.typeck_results().node_args(expr.hir_id); + let args = self.typeck_results.node_args(expr.hir_id); match res { // A regular function, constructor function or a constant. Res::Def(DefKind::Fn, _) @@ -1060,7 +1052,7 @@ impl<'tcx> Cx<'tcx> { let user_provided_types = self.typeck_results.user_provided_types(); let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new); debug!("convert_path_expr: user_ty={:?}", user_ty); - let ty = self.typeck_results().node_type(expr.hir_id); + let ty = self.typeck_results.node_type(expr.hir_id); match ty.kind() { // A unit struct/variant which is used as a value. // We return a completely different ExprKind here to account for this special case. diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index a98c2bb61f666..823f2820f40db 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -16,7 +16,6 @@ use rustc_middle::ty::{self, RvalueScopes, TyCtxt}; use tracing::instrument; use crate::thir::pattern::pat_from_hir; -use crate::thir::util::UserAnnotatedTyHelpers; pub(crate) fn thir_body( tcx: TyCtxt<'_>, @@ -113,7 +112,7 @@ impl<'tcx> Cx<'tcx> { #[instrument(level = "debug", skip(self))] fn pattern_from_hir(&mut self, p: &'tcx hir::Pat<'tcx>) -> Box> { - pat_from_hir(self.tcx, self.typing_env, self.typeck_results(), p) + pat_from_hir(self.tcx, self.typing_env, self.typeck_results, p) } fn closure_env_param(&self, owner_def: LocalDefId, expr_id: HirId) -> Option> { @@ -197,15 +196,12 @@ impl<'tcx> Cx<'tcx> { Param { pat: Some(pat), ty, ty_span, self_kind, hir_id: Some(param.hir_id) } }) } -} - -impl<'tcx> UserAnnotatedTyHelpers<'tcx> for Cx<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - fn typeck_results(&self) -> &ty::TypeckResults<'tcx> { - self.typeck_results + fn user_args_applied_to_ty_of_hir_id( + &self, + hir_id: HirId, + ) -> Option> { + crate::thir::util::user_args_applied_to_ty_of_hir_id(self.typeck_results, hir_id) } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 20a728d6d5b2c..b297afcb4d04b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -27,7 +27,6 @@ use tracing::{debug, instrument}; pub(crate) use self::check_match::check_match; use crate::errors::*; use crate::fluent_generated as fluent; -use crate::thir::util::UserAnnotatedTyHelpers; struct PatCtxt<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -537,16 +536,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { _ => { let e = match res { Res::Def(DefKind::ConstParam, def_id) => { - self.tcx.dcx().emit_err(ConstParamInPattern { - span, - const_span: self.tcx().def_span(def_id), - }) + let const_span = self.tcx.def_span(def_id); + self.tcx.dcx().emit_err(ConstParamInPattern { span, const_span }) } Res::Def(DefKind::Static { .. }, def_id) => { - self.tcx.dcx().emit_err(StaticInPattern { - span, - static_span: self.tcx().def_span(def_id), - }) + let static_span = self.tcx.def_span(def_id); + self.tcx.dcx().emit_err(StaticInPattern { span, static_span }) } _ => self.tcx.dcx().emit_err(NonConstPath { span }), }; @@ -570,6 +565,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { kind } + fn user_args_applied_to_ty_of_hir_id( + &self, + hir_id: hir::HirId, + ) -> Option> { + crate::thir::util::user_args_applied_to_ty_of_hir_id(self.typeck_results, hir_id) + } + /// Takes a HIR Path. If the path is a constant, evaluates it and feeds /// it to `const_to_pat`. Any other path (like enum variants without fields) /// is converted to the corresponding pattern via `lower_variant_or_leaf`. @@ -600,12 +602,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { return pattern; } - let user_provided_types = self.typeck_results().user_provided_types(); + let user_provided_types = self.typeck_results.user_provided_types(); if let Some(&user_ty) = user_provided_types.get(id) { let annotation = CanonicalUserTypeAnnotation { user_ty: Box::new(user_ty), span, - inferred_ty: self.typeck_results().node_type(id), + inferred_ty: self.typeck_results.node_type(id), }; Box::new(Pat { span, @@ -669,13 +671,3 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind } } - -impl<'tcx> UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn typeck_results(&self) -> &ty::TypeckResults<'tcx> { - self.typeck_results - } -} diff --git a/compiler/rustc_mir_build/src/thir/util.rs b/compiler/rustc_mir_build/src/thir/util.rs index ed7c7e4099304..4dff093afd0d9 100644 --- a/compiler/rustc_mir_build/src/thir/util.rs +++ b/compiler/rustc_mir_build/src/thir/util.rs @@ -1,33 +1,27 @@ use rustc_hir as hir; use rustc_middle::bug; -use rustc_middle::ty::{self, CanonicalUserType, TyCtxt}; +use rustc_middle::ty::{self, CanonicalUserType}; use tracing::debug; -pub(crate) trait UserAnnotatedTyHelpers<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx>; - - fn typeck_results(&self) -> &ty::TypeckResults<'tcx>; - - /// Looks up the type associated with this hir-id and applies the - /// user-given generic parameters; the hir-id must map to a suitable - /// type. - fn user_args_applied_to_ty_of_hir_id( - &self, - hir_id: hir::HirId, - ) -> Option> { - let user_provided_types = self.typeck_results().user_provided_types(); - let mut user_ty = *user_provided_types.get(hir_id)?; - debug!("user_subts_applied_to_ty_of_hir_id: user_ty={:?}", user_ty); - let ty = self.typeck_results().node_type(hir_id); - match ty.kind() { - ty::Adt(adt_def, ..) => { - if let ty::UserTypeKind::TypeOf(ref mut did, _) = &mut user_ty.value.kind { - *did = adt_def.did(); - } - Some(user_ty) +/// Looks up the type associated with this hir-id and applies the +/// user-given generic parameters; the hir-id must map to a suitable +/// type. +pub(crate) fn user_args_applied_to_ty_of_hir_id<'tcx>( + typeck_results: &ty::TypeckResults<'tcx>, + hir_id: hir::HirId, +) -> Option> { + let user_provided_types = typeck_results.user_provided_types(); + let mut user_ty = *user_provided_types.get(hir_id)?; + debug!("user_subts_applied_to_ty_of_hir_id: user_ty={:?}", user_ty); + let ty = typeck_results.node_type(hir_id); + match ty.kind() { + ty::Adt(adt_def, ..) => { + if let ty::UserTypeKind::TypeOf(ref mut did, _) = &mut user_ty.value.kind { + *did = adt_def.did(); } - ty::FnDef(..) => Some(user_ty), - _ => bug!("ty: {:?} should not have user provided type {:?} recorded ", ty, user_ty), + Some(user_ty) } + ty::FnDef(..) => Some(user_ty), + _ => bug!("ty: {:?} should not have user provided type {:?} recorded ", ty, user_ty), } } From abd900259da5b8b051c4b5a4526c722fb1295d26 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 4 Feb 2025 15:43:02 +1100 Subject: [PATCH 27/28] Rename `thir::cx::Cx` to `ThirBuildCx` --- compiler/rustc_mir_build/src/thir/cx/block.rs | 4 ++-- compiler/rustc_mir_build/src/thir/cx/expr.rs | 4 ++-- compiler/rustc_mir_build/src/thir/cx/mod.rs | 13 ++++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index c9df027687ab9..e858b629ab14b 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -6,9 +6,9 @@ use rustc_middle::ty; use rustc_middle::ty::CanonicalUserTypeAnnotation; use tracing::debug; -use crate::thir::cx::Cx; +use crate::thir::cx::ThirBuildCx; -impl<'tcx> Cx<'tcx> { +impl<'tcx> ThirBuildCx<'tcx> { pub(crate) fn mirror_block(&mut self, block: &'tcx hir::Block<'tcx>) -> BlockId { // We have to eagerly lower the "spine" of the statements // in order to get the lexical scoping correctly. diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 8eec0fe12d414..54da6924db444 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -21,9 +21,9 @@ use rustc_middle::{bug, span_bug}; use rustc_span::{Span, sym}; use tracing::{debug, info, instrument, trace}; -use crate::thir::cx::Cx; +use crate::thir::cx::ThirBuildCx; -impl<'tcx> Cx<'tcx> { +impl<'tcx> ThirBuildCx<'tcx> { /// Create a THIR expression for the given HIR expression. This expands all /// adjustments and directly adds the type information from the /// `typeck_results`. See the [dev-guide] for more details. diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 823f2820f40db..9d114e6755931 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -17,13 +17,14 @@ use tracing::instrument; use crate::thir::pattern::pat_from_hir; +/// Query implementation for [`TyCtxt::thir_body`]. pub(crate) fn thir_body( tcx: TyCtxt<'_>, owner_def: LocalDefId, ) -> Result<(&Steal>, ExprId), ErrorGuaranteed> { let hir = tcx.hir(); let body = hir.body_owned_by(owner_def); - let mut cx = Cx::new(tcx, owner_def); + let mut cx = ThirBuildCx::new(tcx, owner_def); if let Some(reported) = cx.typeck_results.tainted_by_errors { return Err(reported); } @@ -51,8 +52,10 @@ pub(crate) fn thir_body( Ok((tcx.alloc_steal_thir(cx.thir), expr)) } -struct Cx<'tcx> { +/// Context for lowering HIR to THIR for a single function body (or other kind of body). +struct ThirBuildCx<'tcx> { tcx: TyCtxt<'tcx>, + /// The THIR data that this context is building. thir: Thir<'tcx>, typing_env: ty::TypingEnv<'tcx>, @@ -68,8 +71,8 @@ struct Cx<'tcx> { body_owner: DefId, } -impl<'tcx> Cx<'tcx> { - fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Cx<'tcx> { +impl<'tcx> ThirBuildCx<'tcx> { + fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Self { let typeck_results = tcx.typeck(def); let hir = tcx.hir(); let hir_id = tcx.local_def_id_to_hir_id(def); @@ -93,7 +96,7 @@ impl<'tcx> Cx<'tcx> { BodyTy::Const(typeck_results.node_type(hir_id)) }; - Cx { + Self { tcx, thir: Thir::new(body_type), // FIXME(#132279): We're in a body, we should use a typing From 53b8de1c9a9f027c9d2effc5eeed2fc99cf7068d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:43:49 +0800 Subject: [PATCH 28/28] bootstrap: add wrapper macros for `tracing`-gated tracing macros - Add wrapper macros for `error!`, `warn!`, `info!`, `debug!` and `trace!`, which `cfg(feature = "tracing")`-gates the underlying `tracing` macros. - This is not done for `span!` or `event!` because they can return span guards, and you can't really wrap that. - This is also not possible for `tracing::instrument` attribute proc-macro unless you use another attribute proc-macro to wrap that. --- src/bootstrap/src/bin/main.rs | 12 ++--- src/bootstrap/src/lib.rs | 87 +++++++++++++++--------------- src/bootstrap/src/utils/mod.rs | 2 + src/bootstrap/src/utils/tracing.rs | 49 +++++++++++++++++ 4 files changed, 99 insertions(+), 51 deletions(-) create mode 100644 src/bootstrap/src/utils/tracing.rs diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 5fcf7eda8df79..441674936c666 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -11,12 +11,12 @@ use std::str::FromStr; use std::{env, process}; use bootstrap::{ - Build, CONFIG_CHANGE_HISTORY, Config, Flags, Subcommand, find_recent_config_change_ids, + Build, CONFIG_CHANGE_HISTORY, Config, Flags, Subcommand, debug, find_recent_config_change_ids, human_readable_changes, t, }; use build_helper::ci::CiEnv; #[cfg(feature = "tracing")] -use tracing::{debug, instrument}; +use tracing::instrument; #[cfg_attr(feature = "tracing", instrument(level = "trace", name = "main"))] fn main() { @@ -29,10 +29,8 @@ fn main() { return; } - #[cfg(feature = "tracing")] debug!("parsing flags"); let flags = Flags::parse(&args); - #[cfg(feature = "tracing")] debug!("parsing config based on flags"); let config = Config::parse(flags); @@ -95,7 +93,6 @@ fn main() { let dump_bootstrap_shims = config.dump_bootstrap_shims; let out_dir = config.out.clone(); - #[cfg(feature = "tracing")] debug!("creating new build based on config"); Build::new(config).build(); @@ -207,8 +204,9 @@ fn check_version(config: &Config) -> Option { // Due to the conditional compilation via the `tracing` cargo feature, this means that `tracing` // usages in bootstrap need to be also gated behind the `tracing` feature: // -// - `tracing` macros (like `trace!`) and anything from `tracing`, `tracing_subscriber` and -// `tracing-tree` will need to be gated by `#[cfg(feature = "tracing")]`. +// - `tracing` macros with log levels (`trace!`, `debug!`, `warn!`, `info`, `error`) should not be +// used *directly*. You should use the wrapped `tracing` macros which gate the actual invocations +// behind `feature = "tracing"`. // - `tracing`'s `#[instrument(..)]` macro will need to be gated like `#![cfg_attr(feature = // "tracing", instrument(..))]`. #[cfg(feature = "tracing")] diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index d56f35f866cb8..2dd83d5938e9d 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -28,8 +28,6 @@ use std::{env, fs, io, str}; use build_helper::ci::gha; use build_helper::exit; use termcolor::{ColorChoice, StandardStream, WriteColor}; -#[cfg(feature = "tracing")] -use tracing::{debug, instrument, span, trace}; use utils::build_stamp::BuildStamp; use utils::channel::GitInfo; @@ -46,6 +44,8 @@ pub use core::builder::PathSet; pub use core::config::Config; pub use core::config::flags::{Flags, Subcommand}; +#[cfg(feature = "tracing")] +use tracing::{instrument, span}; pub use utils::change_tracker::{ CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes, }; @@ -541,72 +541,71 @@ impl Build { /// Executes the entire build, as configured by the flags and configuration. #[cfg_attr(feature = "tracing", instrument(level = "debug", name = "Build::build", skip_all))] pub fn build(&mut self) { - #[cfg(feature = "tracing")] trace!("setting up job management"); unsafe { crate::utils::job::setup(self); } - #[cfg(feature = "tracing")] - trace!("downloading rustfmt early"); - // Download rustfmt early so that it can be used in rust-analyzer configs. + trace!("downloading rustfmt early"); let _ = &builder::Builder::new(self).initial_rustfmt(); - #[cfg(feature = "tracing")] - let hardcoded_span = - span!(tracing::Level::DEBUG, "handling hardcoded subcommands (Format, Suggest, Perf)") - .entered(); - - // hardcoded subcommands - match &self.config.cmd { - Subcommand::Format { check, all } => { - return core::build_steps::format::format( - &builder::Builder::new(self), - *check, - *all, - &self.config.paths, - ); - } - Subcommand::Suggest { run } => { - return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run); - } - Subcommand::Perf { .. } => { - return core::build_steps::perf::perf(&builder::Builder::new(self)); - } - _cmd => { - #[cfg(feature = "tracing")] - debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling"); + // Handle hard-coded subcommands. + { + #[cfg(feature = "tracing")] + let _hardcoded_span = span!( + tracing::Level::DEBUG, + "handling hardcoded subcommands (Format, Suggest, Perf)" + ) + .entered(); + + match &self.config.cmd { + Subcommand::Format { check, all } => { + return core::build_steps::format::format( + &builder::Builder::new(self), + *check, + *all, + &self.config.paths, + ); + } + Subcommand::Suggest { run } => { + return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run); + } + Subcommand::Perf { .. } => { + return core::build_steps::perf::perf(&builder::Builder::new(self)); + } + _cmd => { + debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling"); + } } - } - #[cfg(feature = "tracing")] - drop(hardcoded_span); - #[cfg(feature = "tracing")] - debug!("handling subcommand normally"); + debug!("handling subcommand normally"); + } if !self.config.dry_run() { #[cfg(feature = "tracing")] let _real_run_span = span!(tracing::Level::DEBUG, "executing real run").entered(); + // We first do a dry-run. This is a sanity-check to ensure that + // steps don't do anything expensive in the dry-run. { #[cfg(feature = "tracing")] let _sanity_check_span = span!(tracing::Level::DEBUG, "(1) executing dry-run sanity-check").entered(); - - // We first do a dry-run. This is a sanity-check to ensure that - // steps don't do anything expensive in the dry-run. self.config.dry_run = DryRun::SelfCheck; let builder = builder::Builder::new(self); builder.execute_cli(); } - #[cfg(feature = "tracing")] - let _actual_run_span = - span!(tracing::Level::DEBUG, "(2) executing actual run").entered(); - self.config.dry_run = DryRun::Disabled; - let builder = builder::Builder::new(self); - builder.execute_cli(); + // Actual run. + { + #[cfg(feature = "tracing")] + let _actual_run_span = + span!(tracing::Level::DEBUG, "(2) executing actual run").entered(); + self.config.dry_run = DryRun::Disabled; + let builder = builder::Builder::new(self); + builder.execute_cli(); + } } else { #[cfg(feature = "tracing")] let _dry_run_span = span!(tracing::Level::DEBUG, "executing dry run").entered(); diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs index ea56932b40437..caef8ce3088a7 100644 --- a/src/bootstrap/src/utils/mod.rs +++ b/src/bootstrap/src/utils/mod.rs @@ -14,6 +14,8 @@ pub(crate) mod render_tests; pub(crate) mod shared_helpers; pub(crate) mod tarball; +pub(crate) mod tracing; + #[cfg(feature = "build-metrics")] pub(crate) mod metrics; diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs new file mode 100644 index 0000000000000..e89decf9e5586 --- /dev/null +++ b/src/bootstrap/src/utils/tracing.rs @@ -0,0 +1,49 @@ +//! Wrapper macros for `tracing` macros to avoid having to write `cfg(feature = "tracing")`-gated +//! `debug!`/`trace!` everytime, e.g. +//! +//! ```rust,ignore (example) +//! #[cfg(feature = "tracing")] +//! trace!("..."); +//! ``` +//! +//! When `feature = "tracing"` is inactive, these macros expand to nothing. + +#[macro_export] +macro_rules! trace { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::trace!($($tokens)*) + } +} + +#[macro_export] +macro_rules! debug { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::debug!($($tokens)*) + } +} + +#[macro_export] +macro_rules! warn { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::warn!($($tokens)*) + } +} + +#[macro_export] +macro_rules! info { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::info!($($tokens)*) + } +} + +#[macro_export] +macro_rules! error { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::error!($($tokens)*) + } +}