Auto merge of #115979 - GuillaumeGomez:rollup-06ujzgh, r=GuillaumeGomez

Rollup of 6 pull requests

Successful merges:

 - #113383 (style-guide: Add section on bugs, and resolving bugs)
 - #115499 (rustc_target/riscv: Fix passing of transparent unions with only one non-ZST member)
 - #115801 (Detect cycle errors hidden by opaques during monomorphization)
 - #115947 (Custom code classes in docs warning)
 - #115957 (fix mismatched symbols)
 - #115958 (explain mysterious addition in float minimum/maximum)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-09-19 18:52:10 +00:00
commit ac5ac4754a
21 changed files with 385 additions and 147 deletions

View File

@ -1118,6 +1118,10 @@ fn is_tuple(this: TyAndLayout<'tcx>) -> bool {
fn is_unit(this: TyAndLayout<'tcx>) -> bool {
matches!(this.ty.kind(), ty::Tuple(list) if list.len() == 0)
}
fn is_transparent(this: TyAndLayout<'tcx>) -> bool {
matches!(this.ty.kind(), ty::Adt(def, _) if def.repr().transparent())
}
}
/// Calculates whether a function's ABI can unwind or not.

View File

@ -89,6 +89,17 @@ fn should_use_fp_conv_helper<'a, Ty, C>(
}
FieldsShape::Union(_) => {
if !arg_layout.is_zst() {
if arg_layout.is_transparent() {
let non_1zst_elem = arg_layout.non_1zst_field(cx).expect("not exactly one non-1-ZST field in non-ZST repr(transparent) union").1;
return should_use_fp_conv_helper(
cx,
&non_1zst_elem,
xlen,
flen,
field1_kind,
field2_kind,
);
}
return Err(CannotUseFpConv);
}
}

View File

@ -66,6 +66,7 @@ fn ty_and_layout_pointee_info_at(
fn is_never(this: TyAndLayout<'a, Self>) -> bool;
fn is_tuple(this: TyAndLayout<'a, Self>) -> bool;
fn is_unit(this: TyAndLayout<'a, Self>) -> bool;
fn is_transparent(this: TyAndLayout<'a, Self>) -> bool;
}
impl<'a, Ty> TyAndLayout<'a, Ty> {
@ -136,6 +137,13 @@ pub fn is_unit<C>(self) -> bool
Ty::is_unit(self)
}
pub fn is_transparent<C>(self) -> bool
where
Ty: TyAbiInterface<'a, C>,
{
Ty::is_transparent(self)
}
pub fn offset_of_subfield<C>(self, cx: &C, indices: impl Iterator<Item = usize>) -> Size
where
Ty: TyAbiInterface<'a, C>,

View File

@ -3,10 +3,13 @@
use rustc_middle::query::Providers;
use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
use rustc_trait_selection::infer::InferCtxtBuilderExt;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::query::{
normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution,
};
use rustc_trait_selection::traits::{self, ObligationCause, SelectionContext};
use rustc_trait_selection::traits::{
self, FulfillmentErrorCode, ObligationCause, SelectionContext,
};
use std::sync::atomic::Ordering;
pub(crate) fn provide(p: &mut Providers) {
@ -40,6 +43,27 @@ fn normalize_projection_ty<'tcx>(
&mut obligations,
);
ocx.register_obligations(obligations);
// #112047: With projections and opaques, we are able to create opaques that
// are recursive (given some substitution of the opaque's type variables).
// In that case, we may only realize a cycle error when calling
// `normalize_erasing_regions` in mono.
if !ocx.infcx.next_trait_solver() {
let errors = ocx.select_where_possible();
if !errors.is_empty() {
// Rustdoc may attempt to normalize type alias types which are not
// well-formed. Rustdoc also normalizes types that are just not
// well-formed, since we don't do as much HIR analysis (checking
// that impl vars are constrained by the signature, for example).
if !tcx.sess.opts.actually_rustdoc {
for error in &errors {
if let FulfillmentErrorCode::CodeCycle(cycle) = &error.code {
ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle);
}
}
}
return Err(NoSolution);
}
}
// FIXME(associated_const_equality): All users of normalize_projection_ty expected
// a type, but there is the possibility it could've been a const now. Maybe change
// it to a Term later?

View File

@ -957,6 +957,7 @@ pub fn minimum(self, other: f32) -> f32 {
} else if self == other {
if self.is_sign_negative() && other.is_sign_positive() { self } else { other }
} else {
// At least one input is NaN. Use `+` to perform NaN propagation and quieting.
self + other
}
}

View File

@ -968,6 +968,7 @@ pub fn minimum(self, other: f64) -> f64 {
} else if self == other {
if self.is_sign_negative() && other.is_sign_positive() { self } else { other }
} else {
// At least one input is NaN. Use `+` to perform NaN propagation and quieting.
self + other
}
}

View File

@ -134,7 +134,7 @@ pub unsafe fn setup(build: &mut Build) {
// If this failed, well at least we tried! An example of DuplicateHandle
// failing in the past has been when the wrong python2 package spawned this
// build system (e.g., the `python2` package in MSYS instead of
// `mingw-w64-x86_64-python2`. Not sure why it failed, but the "failure
// `mingw-w64-x86_64-python2`). Not sure why it failed, but the "failure
// mode" here is that we only clean everything up when the build system
// dies, not when the python parent does, so not too bad.
if r.is_err() {

View File

@ -32,6 +32,19 @@ This should not be interpreted as forbidding developers from following a
non-default style, or forbidding tools from adding any particular configuration
options.
## Bugs
If the style guide differs from rustfmt, that may represent a bug in rustfmt,
or a bug in the style guide; either way, please report it to the style team or
the rustfmt team or both, for investigation and fix.
If implementing a new formatting tool based on the style guide and default Rust
style, please test it on the corpus of existing Rust code, and avoid causing
widespread breakage. The implementation and testing of such a tool may surface
bugs in either the style guide or rustfmt, as well as bugs in the tool itself.
We typically resolve bugs in a fashion that avoids widespread breakage.
## Formatting conventions
### Indentation and line width

View File

@ -868,7 +868,7 @@ fn error_invalid_codeblock_attr_with_help(
#[derive(Eq, PartialEq, Clone, Debug)]
pub(crate) struct LangString {
original: String,
pub(crate) original: String,
pub(crate) should_panic: bool,
pub(crate) no_run: bool,
pub(crate) ignore: Ignore,
@ -893,11 +893,13 @@ pub(crate) enum Ignore {
/// ```eBNF
/// lang-string = *(token-list / delimited-attribute-list / comment)
///
/// bareword = CHAR *(CHAR)
/// bareword = LEADINGCHAR *(CHAR)
/// bareword-without-leading-char = CHAR *(CHAR)
/// quoted-string = QUOTE *(NONQUOTE) QUOTE
/// token = bareword / quoted-string
/// token-without-leading-char = bareword-without-leading-char / quoted-string
/// sep = COMMA/WS *(COMMA/WS)
/// attribute = (DOT token)/(token EQUAL token)
/// attribute = (DOT token)/(token EQUAL token-without-leading-char)
/// attribute-list = [sep] attribute *(sep attribute) [sep]
/// delimited-attribute-list = OPEN-CURLY-BRACKET attribute-list CLOSE-CURLY-BRACKET
/// token-list = [sep] token *(sep token) [sep]
@ -907,8 +909,15 @@ pub(crate) enum Ignore {
/// CLOSE_PARENT = ")"
/// OPEN-CURLY-BRACKET = "{"
/// CLOSE-CURLY-BRACKET = "}"
/// CHAR = ALPHA / DIGIT / "_" / "-" / ":"
/// QUOTE = %x22
/// LEADINGCHAR = ALPHA | DIGIT | "_" | "-" | ":"
/// ; All ASCII punctuation except comma, quote, equals, backslash, grave (backquote) and braces.
/// ; Comma is used to separate language tokens, so it can't be used in one.
/// ; Quote is used to allow otherwise-disallowed characters in language tokens.
/// ; Equals is used to make key=value pairs in attribute blocks.
/// ; Backslash and grave are special Markdown characters.
/// ; Braces are used to start an attribute block.
/// CHAR = ALPHA | DIGIT | "_" | "-" | ":" | "." | "!" | "#" | "$" | "%" | "&" | "*" | "+" | "/" |
/// ";" | "<" | ">" | "?" | "@" | "^" | "|" | "~"
/// NONQUOTE = %x09 / %x20 / %x21 / %x23-7E ; TAB / SPACE / all printable characters except `"`
/// COMMA = ","
/// DOT = "."
@ -932,9 +941,12 @@ pub(crate) enum LangStringToken<'a> {
KeyValueAttribute(&'a str, &'a str),
}
fn is_bareword_char(c: char) -> bool {
fn is_leading_char(c: char) -> bool {
c == '_' || c == '-' || c == ':' || c.is_ascii_alphabetic() || c.is_ascii_digit()
}
fn is_bareword_char(c: char) -> bool {
is_leading_char(c) || ".!#$%&*+/;<>?@^|~".contains(c)
}
fn is_separator(c: char) -> bool {
c == ' ' || c == ',' || c == '\t'
}
@ -1077,7 +1089,7 @@ fn parse_in_attribute_block(&mut self) -> Option<LangStringToken<'a>> {
return self.next();
} else if c == '.' {
return self.parse_class(pos);
} else if c == '"' || is_bareword_char(c) {
} else if c == '"' || is_leading_char(c) {
return self.parse_key_value(c, pos);
} else {
self.emit_error(format!("unexpected character `{c}`"));
@ -1107,7 +1119,11 @@ fn parse_outside_attribute_block(&mut self, start: usize) -> Option<LangStringTo
return None;
}
let indices = self.parse_string(pos)?;
if let Some((_, c)) = self.inner.peek().copied() && c != '{' && !is_separator(c) && c != '(' {
if let Some((_, c)) = self.inner.peek().copied() &&
c != '{' &&
!is_separator(c) &&
c != '('
{
self.emit_error(format!("expected ` `, `{{` or `,` after `\"`, found `{c}`"));
return None;
}
@ -1115,8 +1131,6 @@ fn parse_outside_attribute_block(&mut self, start: usize) -> Option<LangStringTo
} else if c == '{' {
self.is_in_attribute_block = true;
return self.next();
} else if is_bareword_char(c) {
continue;
} else if is_separator(c) {
if pos != start {
return Some(LangStringToken::LangToken(&self.data[start..pos]));
@ -1130,6 +1144,10 @@ fn parse_outside_attribute_block(&mut self, start: usize) -> Option<LangStringTo
return Some(LangStringToken::LangToken(&self.data[start..pos]));
}
return self.next();
} else if pos == start && is_leading_char(c) {
continue;
} else if pos != start && is_bareword_char(c) {
continue;
} else {
self.emit_error(format!("unexpected character `{c}`"));
return None;
@ -1158,6 +1176,29 @@ fn next(&mut self) -> Option<Self::Item> {
}
}
fn tokens(string: &str) -> impl Iterator<Item = LangStringToken<'_>> {
// Pandoc, which Rust once used for generating documentation,
// expects lang strings to be surrounded by `{}` and for each token
// to be proceeded by a `.`. Since some of these lang strings are still
// loose in the wild, we strip a pair of surrounding `{}` from the lang
// string and a leading `.` from each token.
let string = string.trim();
let first = string.chars().next();
let last = string.chars().last();
let string =
if first == Some('{') && last == Some('}') { &string[1..string.len() - 1] } else { string };
string
.split(|c| c == ',' || c == ' ' || c == '\t')
.map(str::trim)
.map(|token| token.strip_prefix('.').unwrap_or(token))
.filter(|token| !token.is_empty())
.map(|token| LangStringToken::LangToken(token))
}
impl Default for LangString {
fn default() -> Self {
Self {
@ -1208,122 +1249,130 @@ fn parse(
data.original = string.to_owned();
for token in TagIterator::new(string, extra) {
match token {
LangStringToken::LangToken("should_panic") => {
data.should_panic = true;
seen_rust_tags = !seen_other_tags;
}
LangStringToken::LangToken("no_run") => {
data.no_run = true;
seen_rust_tags = !seen_other_tags;
}
LangStringToken::LangToken("ignore") => {
data.ignore = Ignore::All;
seen_rust_tags = !seen_other_tags;
}
LangStringToken::LangToken(x) if x.starts_with("ignore-") => {
if enable_per_target_ignores {
ignores.push(x.trim_start_matches("ignore-").to_owned());
let mut call = |tokens: &mut dyn Iterator<Item = LangStringToken<'_>>| {
for token in tokens {
match token {
LangStringToken::LangToken("should_panic") => {
data.should_panic = true;
seen_rust_tags = !seen_other_tags;
}
}
LangStringToken::LangToken("rust") => {
data.rust = true;
seen_rust_tags = true;
}
LangStringToken::LangToken("custom") => {
if custom_code_classes_in_docs {
seen_custom_tag = true;
} else {
seen_other_tags = true;
LangStringToken::LangToken("no_run") => {
data.no_run = true;
seen_rust_tags = !seen_other_tags;
}
}
LangStringToken::LangToken("test_harness") => {
data.test_harness = true;
seen_rust_tags = !seen_other_tags || seen_rust_tags;
}
LangStringToken::LangToken("compile_fail") => {
data.compile_fail = true;
seen_rust_tags = !seen_other_tags || seen_rust_tags;
data.no_run = true;
}
LangStringToken::LangToken(x) if x.starts_with("edition") => {
data.edition = x[7..].parse::<Edition>().ok();
}
LangStringToken::LangToken(x)
if allow_error_code_check && x.starts_with('E') && x.len() == 5 =>
{
if x[1..].parse::<u32>().is_ok() {
data.error_codes.push(x.to_owned());
LangStringToken::LangToken("ignore") => {
data.ignore = Ignore::All;
seen_rust_tags = !seen_other_tags;
}
LangStringToken::LangToken(x) if x.starts_with("ignore-") => {
if enable_per_target_ignores {
ignores.push(x.trim_start_matches("ignore-").to_owned());
seen_rust_tags = !seen_other_tags;
}
}
LangStringToken::LangToken("rust") => {
data.rust = true;
seen_rust_tags = true;
}
LangStringToken::LangToken("custom") => {
if custom_code_classes_in_docs {
seen_custom_tag = true;
} else {
seen_other_tags = true;
}
}
LangStringToken::LangToken("test_harness") => {
data.test_harness = true;
seen_rust_tags = !seen_other_tags || seen_rust_tags;
} else {
seen_other_tags = true;
}
}
LangStringToken::LangToken(x) if extra.is_some() => {
let s = x.to_lowercase();
if let Some((flag, help)) = if s == "compile-fail"
|| s == "compile_fail"
|| s == "compilefail"
LangStringToken::LangToken("compile_fail") => {
data.compile_fail = true;
seen_rust_tags = !seen_other_tags || seen_rust_tags;
data.no_run = true;
}
LangStringToken::LangToken(x) if x.starts_with("edition") => {
data.edition = x[7..].parse::<Edition>().ok();
}
LangStringToken::LangToken(x)
if allow_error_code_check && x.starts_with('E') && x.len() == 5 =>
{
Some((
"compile_fail",
"the code block will either not be tested if not marked as a rust one \
or won't fail if it compiles successfully",
))
} else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" {
Some((
"should_panic",
"the code block will either not be tested if not marked as a rust one \
or won't fail if it doesn't panic when running",
))
} else if s == "no-run" || s == "no_run" || s == "norun" {
Some((
"no_run",
"the code block will either not be tested if not marked as a rust one \
or will be run (which you might not want)",
))
} else if s == "test-harness" || s == "test_harness" || s == "testharness" {
Some((
"test_harness",
"the code block will either not be tested if not marked as a rust one \
or the code will be wrapped inside a main function",
))
} else {
None
} {
if let Some(extra) = extra {
extra.error_invalid_codeblock_attr_with_help(
format!("unknown attribute `{x}`. Did you mean `{flag}`?"),
help,
);
if x[1..].parse::<u32>().is_ok() {
data.error_codes.push(x.to_owned());
seen_rust_tags = !seen_other_tags || seen_rust_tags;
} else {
seen_other_tags = true;
}
}
seen_other_tags = true;
data.unknown.push(x.to_owned());
}
LangStringToken::LangToken(x) => {
seen_other_tags = true;
data.unknown.push(x.to_owned());
}
LangStringToken::KeyValueAttribute(key, value) => {
if custom_code_classes_in_docs {
if key == "class" {
data.added_classes.push(value.to_owned());
} else if let Some(extra) = extra {
extra.error_invalid_codeblock_attr(format!(
"unsupported attribute `{key}`"
));
LangStringToken::LangToken(x) if extra.is_some() => {
let s = x.to_lowercase();
if let Some((flag, help)) = if s == "compile-fail"
|| s == "compile_fail"
|| s == "compilefail"
{
Some((
"compile_fail",
"the code block will either not be tested if not marked as a rust one \
or won't fail if it compiles successfully",
))
} else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" {
Some((
"should_panic",
"the code block will either not be tested if not marked as a rust one \
or won't fail if it doesn't panic when running",
))
} else if s == "no-run" || s == "no_run" || s == "norun" {
Some((
"no_run",
"the code block will either not be tested if not marked as a rust one \
or will be run (which you might not want)",
))
} else if s == "test-harness" || s == "test_harness" || s == "testharness" {
Some((
"test_harness",
"the code block will either not be tested if not marked as a rust one \
or the code will be wrapped inside a main function",
))
} else {
None
} {
if let Some(extra) = extra {
extra.error_invalid_codeblock_attr_with_help(
format!("unknown attribute `{x}`. Did you mean `{flag}`?"),
help,
);
}
}
} else {
seen_other_tags = true;
data.unknown.push(x.to_owned());
}
LangStringToken::LangToken(x) => {
seen_other_tags = true;
data.unknown.push(x.to_owned());
}
LangStringToken::KeyValueAttribute(key, value) => {
if custom_code_classes_in_docs {
if key == "class" {
data.added_classes.push(value.to_owned());
} else if let Some(extra) = extra {
extra.error_invalid_codeblock_attr(format!(
"unsupported attribute `{key}`"
));
}
} else {
seen_other_tags = true;
}
}
LangStringToken::ClassAttribute(class) => {
data.added_classes.push(class.to_owned());
}
}
LangStringToken::ClassAttribute(class) => {
data.added_classes.push(class.to_owned());
}
}
};
if custom_code_classes_in_docs {
call(&mut TagIterator::new(string, extra).into_iter())
} else {
call(&mut tokens(string))
}
// ignore-foo overrides ignore

View File

@ -226,13 +226,28 @@ fn t(lg: LangString) {
..Default::default()
});
// error
t(LangString { original: "{.first.second}".into(), rust: true, ..Default::default() });
t(LangString {
original: "{.first.second}".into(),
rust: true,
added_classes: vec!["first.second".into()],
..Default::default()
});
// error
t(LangString { original: "{class=first=second}".into(), rust: true, ..Default::default() });
// error
t(LangString { original: "{class=first.second}".into(), rust: true, ..Default::default() });
t(LangString {
original: "{class=first.second}".into(),
rust: true,
added_classes: vec!["first.second".into()],
..Default::default()
});
// error
t(LangString { original: "{class=.first}".into(), rust: true, ..Default::default() });
t(LangString {
original: "{class=.first}".into(),
added_classes: vec![".first".into()],
rust: true,
..Default::default()
});
t(LangString {
original: r#"{class="first"}"#.into(),
added_classes: vec!["first".into()],

View File

@ -21,6 +21,10 @@
};
pub(crate) fn check_custom_code_classes(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
if cx.tcx.features().custom_code_classes_in_docs {
// Nothing to check here if the feature is enabled.
return krate;
}
let mut coll = CustomCodeClassLinter { cx };
coll.fold_crate(krate)
@ -59,7 +63,7 @@ pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item)
let dox = item.attrs.doc_value();
find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true, true);
if !tests.custom_classes_found.is_empty() && !cx.tcx.features().custom_code_classes_in_docs {
if !tests.custom_classes_found.is_empty() {
let span = item.attr_span(cx.tcx);
let sess = &cx.tcx.sess.parse_sess;
let mut err = sess

View File

@ -57,25 +57,23 @@ pub fn foo8() {}
/// ```{class=one=two}
/// main;
/// ```
//~^^^ ERROR unexpected `=`
//~^^^ ERROR unexpected `=` character
pub fn foo9() {}
/// ```{.one.two}
/// main;
/// ```
//~^^^ ERROR unexpected `.` character
pub fn foo10() {}
/// ```{class=.one}
/// ```{class=(one}
/// main;
/// ```
//~^^^ ERROR unexpected `.` character after `=`
//~^^^ ERROR unexpected `(` character after `=`
pub fn foo11() {}
/// ```{class=one.two}
/// main;
/// ```
//~^^^ ERROR unexpected `.` character
pub fn foo12() {}
/// ```{(comment)}

View File

@ -77,37 +77,21 @@ LL | | /// main;
LL | | /// ```
| |_______^
error: unexpected `.` character
--> $DIR/custom_code_classes_in_docs-warning.rs:63:1
error: unexpected `(` character after `=`
--> $DIR/custom_code_classes_in_docs-warning.rs:68:1
|
LL | / /// ```{.one.two}
LL | | /// main;
LL | | /// ```
| |_______^
error: unexpected `.` character after `=`
--> $DIR/custom_code_classes_in_docs-warning.rs:69:1
|
LL | / /// ```{class=.one}
LL | | /// main;
LL | | /// ```
| |_______^
error: unexpected `.` character
--> $DIR/custom_code_classes_in_docs-warning.rs:75:1
|
LL | / /// ```{class=one.two}
LL | / /// ```{class=(one}
LL | | /// main;
LL | | /// ```
| |_______^
error: unexpected character `(`
--> $DIR/custom_code_classes_in_docs-warning.rs:81:1
--> $DIR/custom_code_classes_in_docs-warning.rs:79:1
|
LL | / /// ```{(comment)}
LL | | /// main;
LL | | /// ```
| |_______^
error: aborting due to 13 previous errors
error: aborting due to 11 previous errors

View File

@ -8,3 +8,8 @@
//~| NOTE see issue #79483 <https://github.com/rust-lang/rust/issues/79483>
//~| HELP add `#![feature(custom_code_classes_in_docs)]` to the crate attributes to enable
pub struct Bar;
/// ```ASN.1
/// int main(void) { return 0; }
/// ```
pub struct Bar2;

View File

@ -10,7 +10,6 @@
// Hence there are `cfg` throughout this test to disable parts of it on those targets.
// sparc64: https://github.com/rust-lang/rust/issues/115336
// mips64: https://github.com/rust-lang/rust/issues/115404
// riscv64: https://github.com/rust-lang/rust/issues/115481
// loongarch64: https://github.com/rust-lang/rust/issues/115509
macro_rules! assert_abi_compatible {
@ -110,7 +109,7 @@ mod $name {
test_abi_compatible!(wrap1, $t, Wrapper1<$t>);
test_abi_compatible!(wrap2, $t, Wrapper2<$t>);
test_abi_compatible!(wrap3, $t, Wrapper3<$t>);
#[cfg(not(any(target_arch = "riscv64", target_arch = "loongarch64")))]
#[cfg(not(target_arch = "loongarch64"))]
test_abi_compatible!(wrap4, $t, WrapperUnion<$t>);
}
};

View File

@ -0,0 +1,38 @@
// edition: 2021
// build-fail
//~^^ ERROR overflow evaluating the requirement `<A as Second>::{opaque#0} == _`
#![feature(async_fn_in_trait)]
fn main() {
let _ = async {
A.first().await.second().await;
};
}
pub trait First {
type Second: Second;
async fn first(self) -> Self::Second;
}
struct A;
impl First for A {
type Second = A;
async fn first(self) -> Self::Second {
A
}
}
pub trait Second {
async fn second(self);
}
impl<C> Second for C
where
C: First,
{
async fn second(self) {
self.first().await.second().await;
}
}

View File

@ -0,0 +1,5 @@
error[E0275]: overflow evaluating the requirement `<A as Second>::{opaque#0} == _`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0275`.

View File

@ -0,0 +1,29 @@
// edition: 2021
// build-fail
//~^^ ERROR overflow evaluating the requirement `<() as Recur>::Recur == _`
#![feature(impl_trait_in_assoc_type)]
use core::future::Future;
trait Recur {
type Recur: Future<Output = ()>;
fn recur(self) -> Self::Recur;
}
async fn recur(t: impl Recur) {
t.recur().await;
}
impl Recur for () {
type Recur = impl Future<Output = ()>;
fn recur(self) -> Self::Recur {
async move { recur(self).await; }
}
}
fn main() {
recur(());
}

View File

@ -0,0 +1,5 @@
error[E0275]: overflow evaluating the requirement `<() as Recur>::Recur == _`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0275`.

View File

@ -0,0 +1,40 @@
// edition: 2021
// build-fail
//~^^ ERROR overflow evaluating the requirement `<() as B>::Assoc == _`
#![feature(rustc_attrs)]
#![feature(impl_trait_in_assoc_type)]
#[rustc_coinductive]
trait A {
type Assoc;
fn test() -> Self::Assoc;
}
#[rustc_coinductive]
trait B {
type Assoc;
fn test() -> Self::Assoc;
}
impl<T: A> B for T {
type Assoc = impl Sized;
fn test() -> <Self as B>::Assoc {
<T as A>::test()
}
}
fn main() {
<() as A>::test();
}
impl<T: B> A for T {
type Assoc = impl Sized;
fn test() -> <Self as A>::Assoc {
<T as B>::test()
}
}

View File

@ -0,0 +1,5 @@
error[E0275]: overflow evaluating the requirement `<() as B>::Assoc == _`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0275`.