Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,084 changes: 0 additions & 1,084 deletions library/core/src/error.rs

This file was deleted.

501 changes: 501 additions & 0 deletions library/core/src/error/mod.rs

Large diffs are not rendered by default.

1,314 changes: 1,314 additions & 0 deletions library/core/src/error/provide.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
//
// Library features:
// tidy-alphabetical-start
#![feature(arbitrary_self_types_pointers)]
#![feature(array_ptr_get)]
#![feature(asm_experimental_arch)]
#![feature(bigint_helper_methods)]
Expand Down
70 changes: 69 additions & 1 deletion library/coretests/tests/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,23 @@ impl std::fmt::Display for SomeConcreteType {
}
}

struct Invalid;
#[derive(Debug, PartialEq, Eq)]
struct Valid;

impl std::error::Error for SomeConcreteType {
fn provide<'a>(&'a self, request: &mut Request<'a>) {
request
.provide_ref::<String>(&self.some_string)
.provide_ref::<str>(&self.some_string)
.provide_value_with::<String>(|| "bye".to_owned());
.provide_value_with::<String>(|| "bye".to_owned())
.provide_value_with::<Invalid>(|| panic!("should not be called"));
if request.would_be_satisfied_by_ref_of::<Invalid>() {
panic!("should not be satisfied");
}
if request.would_be_satisfied_by_ref_of::<Valid>() {
request.provide_ref(&Valid);
}
}
}

Expand All @@ -29,6 +40,7 @@ fn test_error_generic_member_access() {
assert_eq!(request_ref::<String>(&*obj).unwrap(), "hello");
assert_eq!(request_value::<String>(&*obj).unwrap(), "bye");
assert_eq!(request_value::<u8>(&obj), None);
assert_eq!(request_ref::<Valid>(&obj), Some(&Valid));
}

// Test the Error.provide and request mechanisms with a by-reference trait object.
Expand All @@ -39,6 +51,7 @@ fn test_request_constructor() {
assert_eq!(request_ref::<String>(&*obj).unwrap(), "hello");
assert_eq!(request_value::<String>(&*obj).unwrap(), "bye");
assert_eq!(request_value::<u8>(&obj), None);
assert_eq!(request_ref::<Valid>(&obj), Some(&Valid));
}

// Test the Error.provide and request mechanisms with a boxed trait object.
Expand All @@ -49,6 +62,7 @@ fn test_error_generic_member_access_boxed() {

assert_eq!(request_ref::<String>(&*obj).unwrap(), "hello");
assert_eq!(request_value::<String>(&*obj).unwrap(), "bye");
assert_eq!(request_ref::<Valid>(&*obj), Some(&Valid));

// NOTE: Box<E> only implements Error when E: Error + Sized, which means we can't pass a
// Box<dyn Error> to request_value.
Expand All @@ -63,4 +77,58 @@ fn test_error_generic_member_access_concrete() {
assert_eq!(request_ref::<String>(&obj).unwrap(), "hello");
assert_eq!(request_value::<String>(&obj).unwrap(), "bye");
assert_eq!(request_value::<u8>(&obj), None);
assert_eq!(request_ref::<Valid>(&obj), Some(&Valid));
}

#[test]
fn test_error_combined_access_concrete() {
let obj = SomeConcreteType { some_string: "hello".to_owned() };

let mut string_val = None;
let mut string_ref = None;
let mut u8_val = None;
let mut valid_ref = None;

MultiRequestBuilder::new()
.with_value::<String>()
.with_ref::<String>()
.with_value::<u8>()
.with_ref::<Valid>()
.request(&obj)
.retrieve_value::<String>(|val| string_val = Some(val))
.retrieve_ref::<String>(|val| string_ref = Some(val))
.retrieve_value::<u8>(|val| u8_val = Some(val))
.retrieve_ref::<Valid>(|val| valid_ref = Some(val));

assert_eq!(string_ref.unwrap(), "hello");
assert_eq!(string_val.unwrap(), "bye");
assert_eq!(u8_val, None);
assert_eq!(valid_ref.unwrap(), Valid);
}

#[test]
fn test_error_combined_access_dyn() {
let obj = SomeConcreteType { some_string: "hello".to_owned() };
let obj: &dyn Error = &obj;

let mut string_val = None;
let mut string_ref = None;
let mut u8_val = None;
let mut valid_ref = None;

MultiRequestBuilder::new()
.with_value::<String>()
.with_ref::<String>()
.with_value::<u8>()
.with_ref::<Valid>()
.request(&obj)
.retrieve_value::<String>(|val| string_val = Some(val))
.retrieve_ref::<String>(|val| string_ref = Some(val))
.retrieve_value::<u8>(|val| u8_val = Some(val))
.retrieve_ref::<Valid>(|val| valid_ref = Some(val));

assert_eq!(string_ref.unwrap(), "hello");
assert_eq!(string_val.unwrap(), "bye");
assert_eq!(u8_val, None);
assert_eq!(valid_ref.unwrap(), Valid);
}
12 changes: 12 additions & 0 deletions src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5871,6 +5871,18 @@ The tracking issue for this feature is: [#99301]

[#99301]: https://github.com/rust-lang/rust/issues/99301

------------------------
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
label: "error_generic_member_access_internals",
description: r##"# `error_generic_member_access_internals`

This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.

------------------------
"##,
default_severity: Severity::Allow,
Expand Down
57 changes: 48 additions & 9 deletions tests/codegen-llvm/error-provide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
//@ compile-flags: -Copt-level=3
#![crate_type = "lib"]
#![feature(error_generic_member_access)]

extern crate core;

use core::error::MultiResponse;
use std::error::Request;
use std::fmt;

Expand Down Expand Up @@ -30,21 +34,56 @@ impl fmt::Display for MyError {
}

impl std::error::Error for MyError {
// CHECK-LABEL: @provide
#[no_mangle]
fn provide<'a>(&'a self, request: &mut Request<'a>) {
// LLVM should be able to optimize multiple .provide_* calls into a switch table
// and eliminate redundant ones, rather than compare one-by-one.

// CHECK-NEXT: start:
// CHECK-NEXT: %[[SCRUTINEE:[^ ]+]] = load i128, ptr
// CHECK-NEXT: switch i128 %[[SCRUTINEE]], label %{{.*}} [
// CHECK-COUNT-3: i128 {{.*}}, label %{{.*}}
// CHECK-NEXT: ]
// In an ideal world, LLVM would be able to generate a jump table here.
// Currently it can not, mostly because it can't prove that the tag id
// is not modified. However, this shouldn't matter much since this
// API shouldn't be called many times - when requesting a large number
// of items, MultiRequestBuilder should be used.
//
// We could make a MIR optimization pass that flattens
// the reads of the tag id - this is sound, since it is mutably borrowed,
// but this has a fairly low cost/benefit ratio - `provide` should
// only be called O(1) times per error constructed, and it's already
// not much slower than constructing the error (and much faster
// if an allocation, backtrace or formatting is involved).
request
.provide_ref::<MyBacktrace1>(&self.backtrace1)
.provide_ref::<MyBacktrace3>(&self.other)
.provide_ref::<MyBacktrace2>(&self.backtrace2)
.provide_ref::<MyBacktrace3>(&self.backtrace3);
}
}

pub fn provide_multi(
e: &dyn std::error::Error,
) -> (Option<&[u8; 0]>, Option<&[u8; 1]>, Option<&[u8; 2]>) {
let (mut bt1, mut bt2, mut bt3) = (None, None, None);
let mut value = core::error::MultiRequestBuilder::new()
.with_ref::<[u8; 0]>()
.with_ref::<[u8; 1]>()
.with_ref::<[u8; 2]>()
.with_ref::<[u8; 3]>()
.with_ref::<[u8; 4]>()
.with_ref::<[u8; 5]>()
.with_ref::<[u8; 6]>()
.with_ref::<[u8; 7]>()
.request(e)
.retrieve_ref(|b| bt1 = Some(b))
.retrieve_ref(|b| bt2 = Some(b))
.retrieve_ref(|b| bt3 = Some(b));
(bt1, bt2, bt3)
}

// Check that the virtual function generated has a switch

// CHECK: define {{.*}}4core5error7provide{{.*}}21ChainRefMultiResponse
// CHECK-NEXT: start:
// CHECK-NEXT: %[[SCRUTINEE:[^ ]+]] = load i128, ptr
// CHECK-NEXT: switch i128 %[[SCRUTINEE]], label %{{.*}} [
// The request we write has 8 arms. However, LLVM puts the "bottom-most" 3 arms in
// a separate branch, before it produces a switch. This still leaves us with
// O(log n) performance [LLVM generates a binary tree for ]
// CHECK-COUNT-5: i128 {{.*}}, label %{{.*}}
// CHECK-NEXT: ]
4 changes: 2 additions & 2 deletions tests/ui/span/issue-71363.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ help: the trait `std::fmt::Display` is not implemented for `MyError`
3 | struct MyError;
| ^^^^^^^^^^^^^^
note: required by a bound in `std::error::Error`
--> $SRC_DIR/core/src/error.rs:LL:COL
--> $SRC_DIR/core/src/error/mod.rs:LL:COL

error[E0277]: `MyError` doesn't implement `Debug`
--> $DIR/issue-71363.rs:4:28
Expand All @@ -20,7 +20,7 @@ error[E0277]: `MyError` doesn't implement `Debug`
|
= note: add `#[derive(Debug)]` to `MyError` or manually `impl Debug for MyError`
note: required by a bound in `std::error::Error`
--> $SRC_DIR/core/src/error.rs:LL:COL
--> $SRC_DIR/core/src/error/mod.rs:LL:COL
help: consider annotating `MyError` with `#[derive(Debug)]`
|
3 + #[derive(Debug)]
Expand Down
Loading