Auto merge of #113484 - matthiaskrgr:rollup-goq2u0d, r=matthiaskrgr

Rollup of 7 pull requests

Successful merges:

 - #112931 (Enable zlib in LLVM on aarch64-apple-darwin)
 - #113158 (tests: unset `RUSTC_LOG_COLOR` in a test)
 - #113173 (CI: include workflow name in concurrency group)
 - #113335 (Reveal opaques in new solver)
 - #113390 (CGU formation tweaks)
 - #113399 (Structurally normalize again for byte string lit pat checking)
 - #113412 (Add basic types to SMIR)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-07-08 18:36:58 +00:00
commit d1389b9b48
22 changed files with 181 additions and 89 deletions

View file

@ -31,7 +31,7 @@ defaults:
run: run:
shell: bash shell: bash
concurrency: concurrency:
group: "${{ ((github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.sha) || github.ref }}" group: "${{ github.workflow }}-${{ ((github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.sha) || github.ref }}"
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
pr: pr:

View file

@ -394,8 +394,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut pat_ty = ty; let mut pat_ty = ty;
if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(..), .. }) = lt.kind { if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(..), .. }) = lt.kind {
let expected = self.structurally_resolve_type(span, expected); let expected = self.structurally_resolve_type(span, expected);
if let ty::Ref(_, inner_ty, _) = expected.kind() if let ty::Ref(_, inner_ty, _) = *expected.kind()
&& matches!(inner_ty.kind(), ty::Slice(_)) && self.try_structurally_resolve_type(span, inner_ty).is_slice()
{ {
let tcx = self.tcx; let tcx = self.tcx;
trace!(?lt.hir_id.local_id, "polymorphic byte string lit"); trace!(?lt.hir_id.local_id, "polymorphic byte string lit");

View file

@ -231,8 +231,8 @@ impl<'tcx> UsageMap<'tcx> {
assert!(self.used_map.insert(user_item, used_items).is_none()); assert!(self.used_map.insert(user_item, used_items).is_none());
} }
pub fn get_user_items(&self, item: MonoItem<'tcx>) -> Option<&[MonoItem<'tcx>]> { pub fn get_user_items(&self, item: MonoItem<'tcx>) -> &[MonoItem<'tcx>] {
self.user_map.get(&item).map(|items| items.as_slice()) self.user_map.get(&item).map(|items| items.as_slice()).unwrap_or(&[])
} }
/// Internally iterate over all inlined items used by `item`. /// Internally iterate over all inlined items used by `item`.

View file

@ -427,9 +427,9 @@ fn merge_codegen_units<'tcx>(
// zero-padded suffixes, which means they are automatically sorted by // zero-padded suffixes, which means they are automatically sorted by
// names. The numeric suffix width depends on the number of CGUs, which // names. The numeric suffix width depends on the number of CGUs, which
// is always greater than zero: // is always greater than zero:
// - [1,9] CGUS: `0`, `1`, `2`, ... // - [1,9] CGUs: `0`, `1`, `2`, ...
// - [10,99] CGUS: `00`, `01`, `02`, ... // - [10,99] CGUs: `00`, `01`, `02`, ...
// - [100,999] CGUS: `000`, `001`, `002`, ... // - [100,999] CGUs: `000`, `001`, `002`, ...
// - etc. // - etc.
// //
// If we didn't zero-pad the sorted-by-name order would be `XYZ-cgu.0`, // If we didn't zero-pad the sorted-by-name order would be `XYZ-cgu.0`,
@ -458,7 +458,7 @@ fn internalize_symbols<'tcx>(
/// used to keep track of that. /// used to keep track of that.
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
enum MonoItemPlacement { enum MonoItemPlacement {
SingleCgu { cgu_name: Symbol }, SingleCgu(Symbol),
MultipleCgus, MultipleCgus,
} }
@ -466,7 +466,7 @@ fn internalize_symbols<'tcx>(
let single_codegen_unit = codegen_units.len() == 1; let single_codegen_unit = codegen_units.len() == 1;
if !single_codegen_unit { if !single_codegen_unit {
for cgu in codegen_units.iter_mut() { for cgu in codegen_units.iter() {
for item in cgu.items().keys() { for item in cgu.items().keys() {
// If there is more than one codegen unit, we need to keep track // If there is more than one codegen unit, we need to keep track
// in which codegen units each monomorphization is placed. // in which codegen units each monomorphization is placed.
@ -474,13 +474,13 @@ fn internalize_symbols<'tcx>(
Entry::Occupied(e) => { Entry::Occupied(e) => {
let placement = e.into_mut(); let placement = e.into_mut();
debug_assert!(match *placement { debug_assert!(match *placement {
MonoItemPlacement::SingleCgu { cgu_name } => cgu_name != cgu.name(), MonoItemPlacement::SingleCgu(cgu_name) => cgu_name != cgu.name(),
MonoItemPlacement::MultipleCgus => true, MonoItemPlacement::MultipleCgus => true,
}); });
*placement = MonoItemPlacement::MultipleCgus; *placement = MonoItemPlacement::MultipleCgus;
} }
Entry::Vacant(e) => { Entry::Vacant(e) => {
e.insert(MonoItemPlacement::SingleCgu { cgu_name: cgu.name() }); e.insert(MonoItemPlacement::SingleCgu(cgu.name()));
} }
} }
} }
@ -490,7 +490,7 @@ fn internalize_symbols<'tcx>(
// For each internalization candidates in each codegen unit, check if it is // For each internalization candidates in each codegen unit, check if it is
// used from outside its defining codegen unit. // used from outside its defining codegen unit.
for cgu in codegen_units { for cgu in codegen_units {
let home_cgu = MonoItemPlacement::SingleCgu { cgu_name: cgu.name() }; let home_cgu = MonoItemPlacement::SingleCgu(cgu.name());
for (item, linkage_and_visibility) in cgu.items_mut() { for (item, linkage_and_visibility) in cgu.items_mut() {
if !internalization_candidates.contains(item) { if !internalization_candidates.contains(item) {
@ -501,20 +501,20 @@ fn internalize_symbols<'tcx>(
if !single_codegen_unit { if !single_codegen_unit {
debug_assert_eq!(mono_item_placements[item], home_cgu); debug_assert_eq!(mono_item_placements[item], home_cgu);
if let Some(user_items) = cx.usage_map.get_user_items(*item) { if cx
if user_items .usage_map
.iter() .get_user_items(*item)
.filter_map(|user_item| { .iter()
// Some user mono items might not have been .filter_map(|user_item| {
// instantiated. We can safely ignore those. // Some user mono items might not have been
mono_item_placements.get(user_item) // instantiated. We can safely ignore those.
}) mono_item_placements.get(user_item)
.any(|placement| *placement != home_cgu) })
{ .any(|placement| *placement != home_cgu)
// Found a user from another CGU, so skip to the next item {
// without marking this one as internal. // Found a user from another CGU, so skip to the next item
continue; // without marking this one as internal.
} continue;
} }
} }

View file

@ -7,7 +7,8 @@
//! //!
//! For now, we are developing everything inside `rustc`, thus, we keep this module private. //! For now, we are developing everything inside `rustc`, thus, we keep this module private.
use crate::stable_mir::{self, ty::TyKind, Context}; use crate::stable_mir::ty::{FloatTy, IntTy, RigidTy, TyKind, UintTy};
use crate::stable_mir::{self, Context};
use rustc_middle::mir; use rustc_middle::mir;
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
@ -69,11 +70,28 @@ pub struct Tables<'tcx> {
impl<'tcx> Tables<'tcx> { impl<'tcx> Tables<'tcx> {
fn rustc_ty_to_ty(&mut self, ty: Ty<'tcx>) -> TyKind { fn rustc_ty_to_ty(&mut self, ty: Ty<'tcx>) -> TyKind {
match ty.kind() { match ty.kind() {
ty::Bool => TyKind::Bool, ty::Bool => TyKind::RigidTy(RigidTy::Bool),
ty::Char => todo!(), ty::Char => TyKind::RigidTy(RigidTy::Char),
ty::Int(_) => todo!(), ty::Int(int_ty) => match int_ty {
ty::Uint(_) => todo!(), ty::IntTy::Isize => TyKind::RigidTy(RigidTy::Int(IntTy::Isize)),
ty::Float(_) => todo!(), ty::IntTy::I8 => TyKind::RigidTy(RigidTy::Int(IntTy::I8)),
ty::IntTy::I16 => TyKind::RigidTy(RigidTy::Int(IntTy::I16)),
ty::IntTy::I32 => TyKind::RigidTy(RigidTy::Int(IntTy::I32)),
ty::IntTy::I64 => TyKind::RigidTy(RigidTy::Int(IntTy::I64)),
ty::IntTy::I128 => TyKind::RigidTy(RigidTy::Int(IntTy::I128)),
},
ty::Uint(uint_ty) => match uint_ty {
ty::UintTy::Usize => TyKind::RigidTy(RigidTy::Uint(UintTy::Usize)),
ty::UintTy::U8 => TyKind::RigidTy(RigidTy::Uint(UintTy::U8)),
ty::UintTy::U16 => TyKind::RigidTy(RigidTy::Uint(UintTy::U16)),
ty::UintTy::U32 => TyKind::RigidTy(RigidTy::Uint(UintTy::U32)),
ty::UintTy::U64 => TyKind::RigidTy(RigidTy::Uint(UintTy::U64)),
ty::UintTy::U128 => TyKind::RigidTy(RigidTy::Uint(UintTy::U128)),
},
ty::Float(float_ty) => match float_ty {
ty::FloatTy::F32 => TyKind::RigidTy(RigidTy::Float(FloatTy::F32)),
ty::FloatTy::F64 => TyKind::RigidTy(RigidTy::Float(FloatTy::F64)),
},
ty::Adt(_, _) => todo!(), ty::Adt(_, _) => todo!(),
ty::Foreign(_) => todo!(), ty::Foreign(_) => todo!(),
ty::Str => todo!(), ty::Str => todo!(),
@ -90,9 +108,9 @@ impl<'tcx> Tables<'tcx> {
ty::GeneratorWitness(_) => todo!(), ty::GeneratorWitness(_) => todo!(),
ty::GeneratorWitnessMIR(_, _) => todo!(), ty::GeneratorWitnessMIR(_, _) => todo!(),
ty::Never => todo!(), ty::Never => todo!(),
ty::Tuple(fields) => { ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple(
TyKind::Tuple(fields.iter().map(|ty| self.intern_ty(ty)).collect()) fields.iter().map(|ty| self.intern_ty(ty)).collect(),
} )),
ty::Alias(_, _) => todo!(), ty::Alias(_, _) => todo!(),
ty::Param(_) => todo!(), ty::Param(_) => todo!(),
ty::Bound(_, _) => todo!(), ty::Bound(_, _) => todo!(),

View file

@ -9,7 +9,43 @@ impl Ty {
} }
} }
#[derive(Clone, Debug)]
pub enum TyKind { pub enum TyKind {
RigidTy(RigidTy),
}
#[derive(Clone, Debug)]
pub enum RigidTy {
Bool, Bool,
Char,
Int(IntTy),
Uint(UintTy),
Float(FloatTy),
Tuple(Vec<Ty>), Tuple(Vec<Ty>),
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum IntTy {
Isize,
I8,
I16,
I32,
I64,
I128,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UintTy {
Usize,
U8,
U16,
U32,
U64,
U128,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FloatTy {
F32,
F64,
}

View file

@ -167,9 +167,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
// We don't normalize opaque types unless we have // We don't normalize opaque types unless we have
// `Reveal::All`, even if we're in the defining scope. // `Reveal::All`, even if we're in the defining scope.
let data = match *ty.kind() { let data = match *ty.kind() {
ty::Alias(kind, alias_ty) if kind != ty::Opaque || reveal == Reveal::UserFacing => { ty::Alias(kind, alias_ty) if kind != ty::Opaque || reveal == Reveal::All => alias_ty,
alias_ty
}
_ => return ty.try_super_fold_with(self), _ => return ty.try_super_fold_with(self),
}; };

View file

@ -1,4 +1,4 @@
Change this file to make users of the `download-ci-llvm` configuration download Change this file to make users of the `download-ci-llvm` configuration download
a new version of LLVM from CI, even if the LLVM submodule hasnt changed. a new version of LLVM from CI, even if the LLVM submodule hasnt changed.
Last change is for: https://github.com/rust-lang/rust/pull/96971 Last change is for: https://github.com/rust-lang/rust/pull/112931

View file

@ -352,7 +352,7 @@ impl Step for Llvm {
// Disable zstd to avoid a dependency on libzstd.so. // Disable zstd to avoid a dependency on libzstd.so.
cfg.define("LLVM_ENABLE_ZSTD", "OFF"); cfg.define("LLVM_ENABLE_ZSTD", "OFF");
if target != "aarch64-apple-darwin" && !target.contains("windows") { if !target.contains("windows") {
cfg.define("LLVM_ENABLE_ZLIB", "ON"); cfg.define("LLVM_ENABLE_ZLIB", "ON");
} else { } else {
cfg.define("LLVM_ENABLE_ZLIB", "OFF"); cfg.define("LLVM_ENABLE_ZLIB", "OFF");

View file

@ -302,7 +302,7 @@ concurrency:
# For a given workflow, if we push to the same branch, cancel all previous builds on that branch. # For a given workflow, if we push to the same branch, cancel all previous builds on that branch.
# We add an exception for try builds (try branch) and unrolled rollup builds (try-perf), which # We add an exception for try builds (try branch) and unrolled rollup builds (try-perf), which
# are all triggered on the same branch, but which should be able to run concurrently. # are all triggered on the same branch, but which should be able to run concurrently.
group: ${{ ((github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.sha) || github.ref }} group: ${{ github.workflow }}-${{ ((github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.sha) || github.ref }}
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:

View file

@ -7,6 +7,7 @@
// edition: 2021 // edition: 2021
#![feature(rustc_private)] #![feature(rustc_private)]
#![feature(assert_matches)]
extern crate rustc_driver; extern crate rustc_driver;
extern crate rustc_hir; extern crate rustc_hir;
@ -21,6 +22,7 @@ use rustc_interface::{interface, Queries};
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_session::EarlyErrorHandler; use rustc_session::EarlyErrorHandler;
use rustc_smir::{rustc_internal, stable_mir}; use rustc_smir::{rustc_internal, stable_mir};
use std::assert_matches::assert_matches;
use std::io::Write; use std::io::Write;
const CRATE_NAME: &str = "input"; const CRATE_NAME: &str = "input";
@ -65,6 +67,36 @@ fn test_stable_mir(tcx: TyCtxt<'_>) {
other => panic!("{other:?}"), other => panic!("{other:?}"),
} }
let types = get_item(tcx, &items, (DefKind::Fn, "types")).unwrap();
let body = types.body();
assert_eq!(body.locals.len(), 6);
assert_matches!(
body.locals[0].kind(),
stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Bool)
);
assert_matches!(
body.locals[1].kind(),
stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Bool)
);
assert_matches!(
body.locals[2].kind(),
stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Char)
);
assert_matches!(
body.locals[3].kind(),
stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Int(stable_mir::ty::IntTy::I32))
);
assert_matches!(
body.locals[4].kind(),
stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Uint(stable_mir::ty::UintTy::U64))
);
assert_matches!(
body.locals[5].kind(),
stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Float(
stable_mir::ty::FloatTy::F64
))
);
let drop = get_item(tcx, &items, (DefKind::Fn, "drop")).unwrap(); let drop = get_item(tcx, &items, (DefKind::Fn, "drop")).unwrap();
let body = drop.body(); let body = drop.body();
assert_eq!(body.blocks.len(), 2); assert_eq!(body.blocks.len(), 2);
@ -156,6 +188,10 @@ fn generate_input(path: &str) -> std::io::Result<()> {
x_64.wrapping_add(y_64) x_64.wrapping_add(y_64)
}} }}
pub fn types(b: bool, _: char, _: i32, _: u64, _: f64) -> bool {{
b
}}
pub fn drop(_: String) {{}} pub fn drop(_: String) {{}}
pub fn assert(x: i32) -> i32 {{ pub fn assert(x: i32) -> i32 {{

View file

@ -1,30 +0,0 @@
// check-pass
// edition: 2021
// [next] compile-flags: -Zlower-impl-trait-in-trait-to-assoc-ty
// revisions: current next
#![feature(async_fn_in_trait)]
#![feature(impl_trait_in_assoc_type)]
#![allow(incomplete_features)]
use std::future::Future;
trait MyTrait {
type Fut<'a>: Future<Output = i32>
where
Self: 'a;
fn foo<'a>(&'a self) -> Self::Fut<'a>;
}
impl MyTrait for i32 {
type Fut<'a> = impl Future<Output = i32> + 'a
where
Self: 'a;
fn foo<'a>(&'a self) -> Self::Fut<'a> {
async { *self }
}
}
fn main() {}

View file

@ -1,4 +1,5 @@
// run-pass // run-pass
// unset-rustc-env:RUSTC_LOG_COLOR
// Regression test for https://github.com/rust-lang/rust/issues/73431. // Regression test for https://github.com/rust-lang/rust/issues/73431.

View file

@ -1,5 +1,5 @@
error[E0282]: type annotations needed error[E0282]: type annotations needed
--> $DIR/param-env-region-infer.rs:16:10 --> $DIR/param-env-region-infer.rs:18:10
| |
LL | t as _ LL | t as _
| ^ cannot infer type | ^ cannot infer type

View file

@ -0,0 +1,29 @@
error[E0391]: cycle detected when computing type of `make_dyn_star::{opaque#0}`
--> $DIR/param-env-region-infer.rs:16:60
|
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires type-checking `make_dyn_star`...
--> $DIR/param-env-region-infer.rs:16:1
|
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires computing layout of `make_dyn_star::{opaque#0}`...
= note: ...which requires normalizing `make_dyn_star::{opaque#0}`...
= note: ...which again requires computing type of `make_dyn_star::{opaque#0}`, completing the cycle
note: cycle used when checking item types in top-level module
--> $DIR/param-env-region-infer.rs:10:1
|
LL | / #![feature(dyn_star, pointer_like_trait)]
LL | | #![allow(incomplete_features)]
LL | |
LL | | use std::fmt::Debug;
... |
LL | |
LL | | fn main() {}
| |____________^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0391`.

View file

@ -1,6 +1,7 @@
// revisions: current next // revisions: current next
//[next] compile-flags: -Ztrait-solver=next // Need `-Zdeduplicate-diagnostics=yes` because the number of cycle errors
//[next] check-pass // emitted is for some horrible reason platform-specific.
//[next] compile-flags: -Ztrait-solver=next -Zdeduplicate-diagnostics=yes
// incremental // incremental
// checks that we don't ICE if there are region inference variables in the environment // checks that we don't ICE if there are region inference variables in the environment
@ -13,6 +14,7 @@ use std::fmt::Debug;
use std::marker::PointerLike; use std::marker::PointerLike;
fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
//[next]~^ ERROR cycle detected when computing
t as _ t as _
//[current]~^ ERROR type annotations needed //[current]~^ ERROR type annotations needed
} }

View file

@ -0,0 +1,11 @@
// build-pass
// revisions: current next
//[next] compile-flags: -Ztrait-solver=next
fn test() -> Option<impl Sized> {
Some("")
}
fn main() {
test();
}

View file

@ -1,6 +1,7 @@
// build-fail // build-fail
// compile-flags: -Copt-level=0 // compile-flags: -Copt-level=0
// normalize-stderr-test: ".nll/" -> "/" // normalize-stderr-test: ".nll/" -> "/"
// ignore-compare-mode-next-solver (hangs)
fn main() { fn main() {
rec(Empty); rec(Empty);

View file

@ -1,11 +1,11 @@
error: reached the recursion limit while instantiating `rec::<&mut &mut &mut &mut &mut ...>` error: reached the recursion limit while instantiating `rec::<&mut &mut &mut &mut &mut ...>`
--> $DIR/issue-67552.rs:29:9 --> $DIR/issue-67552.rs:30:9
| |
LL | rec(identity(&mut it)) LL | rec(identity(&mut it))
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
| |
note: `rec` defined here note: `rec` defined here
--> $DIR/issue-67552.rs:22:1 --> $DIR/issue-67552.rs:23:1
| |
LL | / fn rec<T>(mut it: T) LL | / fn rec<T>(mut it: T)
LL | | where LL | | where

View file

@ -3,6 +3,7 @@
// compile-flags: -Copt-level=0 // compile-flags: -Copt-level=0
// dont-check-failure-status // dont-check-failure-status
// dont-check-compiler-stderr // dont-check-compiler-stderr
// ignore-compare-mode-next-solver (hangs)
pub fn encode_num<Writer: ExampleWriter>(n: u32, mut writer: Writer) -> Result<(), Writer::Error> { pub fn encode_num<Writer: ExampleWriter>(n: u32, mut writer: Writer) -> Result<(), Writer::Error> {
if n > 15 { if n > 15 {

View file

@ -1,5 +1,5 @@
// compile-flags: -Ztrait-solver=next // compile-flags: -Ztrait-solver=next
// known-bug: rust-lang/trait-system-refactor-initiative#38 // check-pass
fn test(s: &[u8]) { fn test(s: &[u8]) {
match &s[0..3] { match &s[0..3] {

View file

@ -1,11 +0,0 @@
error[E0271]: type mismatch resolving `[u8; 3] <: <[u8] as Index<Range<usize>>>::Output`
--> $DIR/slice-match-byte-lit.rs:6:9
|
LL | match &s[0..3] {
| -------- this expression has type `&<[u8] as Index<std::ops::Range<usize>>>::Output`
LL | b"uwu" => {}
| ^^^^^^ types differ
error: aborting due to previous error
For more information about this error, try `rustc --explain E0271`.