Merge pull request #587 from dtolnay/trivial

Join trivial reasons in one message
This commit is contained in:
David Tolnay 2020-12-21 13:22:48 -08:00 committed by GitHub
commit 8a01b9d1a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 172 additions and 76 deletions

View File

@ -4,10 +4,10 @@ use crate::gen::out::OutFile;
use crate::gen::{builtin, include, Opt};
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::symbol::Symbol;
use crate::syntax::trivial::TrivialReason;
use crate::syntax::trivial::{self, TrivialReason};
use crate::syntax::{
derive, mangle, Api, Enum, ExternFn, ExternType, Pair, RustName, Signature, Struct, Trait,
Type, Types, Var,
Type, TypeAlias, Types, Var,
};
use proc_macro2::Ident;
use std::collections::{HashMap, HashSet};
@ -119,7 +119,7 @@ fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
for api in apis {
if let Api::TypeAlias(ety) = api {
if let Some(reasons) = out.types.required_trivial.get(&ety.name.rust) {
check_trivial_extern_type(out, &ety.name, reasons)
check_trivial_extern_type(out, ety, reasons)
}
}
}
@ -370,7 +370,7 @@ fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
}
}
fn check_trivial_extern_type(out: &mut OutFile, id: &Pair, reasons: &[TrivialReason]) {
fn check_trivial_extern_type(out: &mut OutFile, alias: &TypeAlias, reasons: &[TrivialReason]) {
// NOTE: The following static assertion is just nice-to-have and not
// necessary for soundness. That's because triviality is always declared by
// the user in the form of an unsafe impl of cxx::ExternType:
@ -404,18 +404,16 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &Pair, reasons: &[TrivialRea
// + struct rust::IsRelocatable<MyType> : std::true_type {};
//
let id = id.to_fully_qualified();
let id = alias.name.to_fully_qualified();
out.builtin.relocatable = true;
for reason in reasons {
writeln!(out, "static_assert(");
writeln!(out, " ::rust::IsRelocatable<{}>::value,", id);
writeln!(
out,
" \"type {} is not move constructible and trivially destructible in C++ yet is used as a trivial type in Rust ({})\");",
id.trim_start_matches("::"),
reason,
);
}
writeln!(out, "static_assert(");
writeln!(out, " ::rust::IsRelocatable<{}>::value,", id);
writeln!(
out,
" \"type {} should be trivially move constructible and trivially destructible in C++ to be used as {} in Rust\");",
id.trim_start_matches("::"),
trivial::as_what(&alias.name, reasons),
);
}
fn write_struct_operator_decls<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {

View File

@ -1,8 +1,8 @@
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::report::Errors;
use crate::syntax::{
error, ident, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Signature,
SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types,
error, ident, trivial, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref,
Signature, SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types,
};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{quote, ToTokens};
@ -325,14 +325,11 @@ fn check_api_type(cx: &mut Check, ety: &ExternType) {
}
if let Some(reasons) = cx.types.required_trivial.get(&ety.name.rust) {
for reason in reasons {
let what = reason.describe_in_context(&ety);
let msg = format!(
"needs a cxx::ExternType impl in order to be used as {}",
what,
);
cx.error(ety, msg);
}
let msg = format!(
"needs a cxx::ExternType impl in order to be used as {}",
trivial::as_what(&ety.name, reasons),
);
cx.error(ety, msg);
}
}

View File

@ -50,11 +50,21 @@ mod ordered {
}
}
impl<'a, T> OrderedSet<&'a T> {
pub fn is_empty(&self) -> bool {
self.vec.is_empty()
}
pub fn iter(&self) -> Iter<'_, 'a, T> {
Iter(self.vec.iter())
}
}
impl<'s, 'a, T> IntoIterator for &'s OrderedSet<&'a T> {
type Item = &'a T;
type IntoIter = Iter<'s, 'a, T>;
fn into_iter(self) -> Self::IntoIter {
Iter(self.vec.iter())
self.iter()
}
}
}

View File

@ -1,8 +1,8 @@
use crate::syntax::set::OrderedSet as Set;
use crate::syntax::{Api, Enum, ExternFn, ExternType, RustName, Struct, Type};
use crate::syntax::{Api, Enum, ExternFn, Pair, RustName, Struct, Type};
use proc_macro2::Ident;
use std::collections::BTreeMap as Map;
use std::fmt::Display;
use std::fmt::{self, Display};
#[derive(Copy, Clone)]
pub enum TrivialReason<'a> {
@ -101,31 +101,146 @@ pub fn required_trivial_reasons<'a>(
required_trivial
}
impl<'a> TrivialReason<'a> {
pub fn describe_in_context(&self, ety: &ExternType) -> String {
match self {
TrivialReason::BoxTarget => format!("Box<{}>", ety.name.rust),
TrivialReason::VecElement => format!("a vector element in Vec<{}>", ety.name.rust),
_ => self.to_string(),
}
// Context:
// "type {type} should be trivially move constructible and trivially destructible in C++ to be used as {what} in Rust"
// "needs a cxx::ExternType impl in order to be used as {what}"
pub fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display + 'a {
struct Description<'a> {
name: &'a Pair,
reasons: &'a [TrivialReason<'a>],
}
}
impl<'a> Display for TrivialReason<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TrivialReason::StructField(strct) => write!(f, "a field of `{}`", strct.name.rust),
TrivialReason::FunctionArgument(efn) => write!(f, "an argument of `{}`", efn.name.rust),
TrivialReason::FunctionReturn(efn) => {
write!(f, "a return value of `{}`", efn.name.rust)
impl<'a> Display for Description<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut field_of = Set::new();
let mut argument_of = Set::new();
let mut return_of = Set::new();
let mut box_target = false;
let mut vec_element = false;
let mut unpinned_mut = Set::new();
for reason in self.reasons {
match reason {
TrivialReason::StructField(strct) => {
field_of.insert(&strct.name.rust);
}
TrivialReason::FunctionArgument(efn) => {
argument_of.insert(&efn.name.rust);
}
TrivialReason::FunctionReturn(efn) => {
return_of.insert(&efn.name.rust);
}
TrivialReason::BoxTarget => box_target = true,
TrivialReason::VecElement => vec_element = true,
TrivialReason::UnpinnedMutArg(efn) => {
unpinned_mut.insert(&efn.name.rust);
}
}
}
TrivialReason::BoxTarget => write!(f, "in a Box<...>"),
TrivialReason::VecElement => write!(f, "a Vec<...> element"),
TrivialReason::UnpinnedMutArg(efn) => write!(
f,
"a non-pinned mutable reference argument of {}",
efn.name.rust
),
let mut clauses = Vec::new();
if !field_of.is_empty() {
clauses.push(Clause::Set {
article: "a",
desc: "field of",
set: &field_of,
});
}
if !argument_of.is_empty() {
clauses.push(Clause::Set {
article: "an",
desc: "argument of",
set: &argument_of,
});
}
if !return_of.is_empty() {
clauses.push(Clause::Set {
article: "a",
desc: "return value of",
set: &return_of,
});
}
if box_target {
clauses.push(Clause::Ty1 {
article: "type",
desc: "Box",
param: self.name,
});
}
if vec_element {
clauses.push(Clause::Ty1 {
article: "a",
desc: "vector element in Vec",
param: self.name,
});
}
if !unpinned_mut.is_empty() {
clauses.push(Clause::Set {
article: "a",
desc: "non-pinned mutable reference argument of",
set: &unpinned_mut,
});
}
for (i, clause) in clauses.iter().enumerate() {
if i == 0 {
write!(f, "{} ", clause.article())?;
} else if i + 1 < clauses.len() {
write!(f, ", ")?;
} else {
write!(f, " or ")?;
}
clause.fmt(f)?;
}
Ok(())
}
}
enum Clause<'a> {
Set {
article: &'a str,
desc: &'a str,
set: &'a Set<&'a Ident>,
},
Ty1 {
article: &'a str,
desc: &'a str,
param: &'a Pair,
},
}
impl<'a> Clause<'a> {
fn article(&self) -> &'a str {
match self {
Clause::Set { article, .. } | Clause::Ty1 { article, .. } => article,
}
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Clause::Set {
article: _,
desc,
set,
} => {
write!(f, "{} ", desc)?;
for (i, ident) in set.iter().take(3).enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "`{}`", ident)?;
}
Ok(())
}
Clause::Ty1 {
article: _,
desc,
param,
} => write!(f, "{}<{}>", desc, param.rust),
}
}
}
Description { name, reasons }
}

View File

@ -16,19 +16,7 @@ error: using C++ string by value is not supported
6 | s: CxxString,
| ^^^^^^^^^^^^
error: needs a cxx::ExternType impl in order to be used as a field of `S`
--> $DIR/by_value_not_supported.rs:10:9
|
10 | type C;
| ^^^^^^
error: needs a cxx::ExternType impl in order to be used as an argument of `f`
--> $DIR/by_value_not_supported.rs:10:9
|
10 | type C;
| ^^^^^^
error: needs a cxx::ExternType impl in order to be used as a return value of `f`
error: needs a cxx::ExternType impl in order to be used as a field of `S`, argument of `f` or return value of `f`
--> $DIR/by_value_not_supported.rs:10:9
|
10 | type C;

View File

@ -16,19 +16,7 @@ error: mutable reference to C++ type requires a pin -- use Pin<&mut CxxVector<..
9 | fn v(v: &mut CxxVector<u8>);
| ^^^^^^^^^^^^^^^^^^
error: needs a cxx::ExternType impl in order to be used as a non-pinned mutable reference argument of f
--> $DIR/pin_mut_opaque.rs:4:9
|
4 | type Opaque;
| ^^^^^^^^^^^
error: needs a cxx::ExternType impl in order to be used as a non-pinned mutable reference argument of g
--> $DIR/pin_mut_opaque.rs:4:9
|
4 | type Opaque;
| ^^^^^^^^^^^
error: needs a cxx::ExternType impl in order to be used as a non-pinned mutable reference argument of h
error: needs a cxx::ExternType impl in order to be used as a non-pinned mutable reference argument of `f`, `g`, `h`
--> $DIR/pin_mut_opaque.rs:4:9
|
4 | type Opaque;