Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving lowering error types #431

Merged
merged 3 commits into from
Feb 21, 2024
Merged
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
11 changes: 7 additions & 4 deletions core/src/hir/elision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,20 +244,23 @@ impl<'ast> BaseLifetimeLowerer<'ast> {

impl<'ast> SelfParamLifetimeLowerer<'ast> {
/// Returns a new [`SelfParamLifetimeLowerer`].
pub fn new(lifetime_env: &'ast ast::LifetimeEnv, ctx: &mut LoweringContext) -> Option<Self> {
let mut hir_nodes = Some(SmallVec::new());
pub fn new(
lifetime_env: &'ast ast::LifetimeEnv,
ctx: &mut LoweringContext,
) -> Result<Self, ()> {
let mut hir_nodes = Ok(SmallVec::new());

for ast_node in lifetime_env.nodes.iter() {
let lifetime = ctx.lower_ident(ast_node.lifetime.name(), "named lifetime");
match (lifetime, &mut hir_nodes) {
(Some(lifetime), Some(hir_nodes)) => {
(Ok(lifetime), Ok(hir_nodes)) => {
hir_nodes.push(BoundedLifetime::new(
lifetime,
ast_node.longer.iter().map(|i| Lifetime::new(*i)).collect(),
ast_node.shorter.iter().map(|i| Lifetime::new(*i)).collect(),
));
}
_ => hir_nodes = None,
_ => hir_nodes = Err(()),
}
}

Expand Down
405 changes: 204 additions & 201 deletions core/src/hir/lowering.rs

Large diffs are not rendered by default.

58 changes: 26 additions & 32 deletions core/src/hir/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::collections::BTreeSet;
use std::fmt::{self, Write};
use std::ops::Deref;

use smallvec::SmallVec;

Expand Down Expand Up @@ -29,14 +30,15 @@ pub struct Method {
pub enum SuccessType {
Writeable,
OutType(OutType),
Unit,
}

/// Whether or not the method returns a value or a result.
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)] // this only exists for fallible/infallible, breaking changes for more complex returns are ok
pub enum ReturnType {
Infallible(Option<SuccessType>),
Fallible(Option<SuccessType>, Option<OutType>),
Infallible(SuccessType),
Fallible(SuccessType, Option<OutType>),
}

/// The `self` parameter of a method.
Expand Down Expand Up @@ -92,44 +94,42 @@ pub struct BorrowingField<'m> {
}

impl SuccessType {
/// Returns `true` if it's writeable, otherwise `false`.
/// Returns whether the variant is `Writeable`.
pub fn is_writeable(&self) -> bool {
matches!(self, SuccessType::Writeable)
}

/// Returns a return type, if it's not a writeable.
/// Returns whether the variant is `Unit`.
pub fn is_unit(&self) -> bool {
matches!(self, SuccessType::Unit)
}

pub fn as_type(&self) -> Option<&OutType> {
match self {
SuccessType::Writeable => None,
SuccessType::OutType(ty) => Some(ty),
_ => None,
}
}
}

impl ReturnType {
/// Returns `true` if it's writeable, otherwise `false`.
pub fn is_writeable(&self) -> bool {
self.return_type()
.map(SuccessType::is_writeable)
.unwrap_or(false)
}
impl Deref for ReturnType {
type Target = SuccessType;

/// Returns the [`ReturnOk`] value, whether it's the single return type or
/// the `Ok` variant of a result.
pub fn return_type(&self) -> Option<&SuccessType> {
fn deref(&self) -> &Self::Target {
match self {
ReturnType::Infallible(ret) | ReturnType::Fallible(ret, _) => ret.as_ref(),
ReturnType::Infallible(ret) | ReturnType::Fallible(ret, _) => ret,
}
}
}

/// Returns `true` if the FFI function returns a value (such that it may be assigned to a variable).
pub fn returns_value(&self) -> bool {
match self {
ReturnType::Fallible(_, _) => true,
ReturnType::Infallible(Some(SuccessType::OutType(_))) => true,
ReturnType::Infallible(Some(SuccessType::Writeable)) => false,
ReturnType::Infallible(None) => false,
}
impl ReturnType {
/// Returns `true` if the FFI function returns `void`. Not that this is different from `is_unit`,
/// which will be true for `DiplomatResult<(), E>` and false for infallible writeable.
pub fn is_ffi_unit(&self) -> bool {
matches!(
self,
ReturnType::Infallible(SuccessType::Unit | SuccessType::Writeable)
)
}

/// Get the list of method lifetimes actually used by the method return type
Expand All @@ -148,9 +148,9 @@ impl ReturnType {
};

match self {
ReturnType::Infallible(Some(SuccessType::OutType(ref ty))) => add_to_set(ty),
ReturnType::Infallible(SuccessType::OutType(ref ty)) => add_to_set(ty),
ReturnType::Fallible(ref ok, ref err) => {
if let Some(SuccessType::OutType(ref ty)) = ok {
if let SuccessType::OutType(ref ty) = ok {
add_to_set(ty)
}
if let Some(ref ty) = err {
Expand Down Expand Up @@ -191,12 +191,6 @@ impl Param {
}

impl Method {
/// Returns `true` if the method takes a writeable as an out parameter,
/// otherwise `false`.
pub fn is_writeable(&self) -> bool {
self.output.is_writeable()
}

/// Returns a fresh [`Lifetimes`] corresponding to `self`.
pub fn method_lifetimes(&self) -> Lifetimes {
self.lifetime_env.lifetimes()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,23 @@ TypeContext {
},
],
output: Infallible(
Some(
OutType(
Struct(
OutStruct(
StructPath {
lifetimes: Lifetimes {
indices: [
NonStatic(
Lifetime(
0,
),
OutType(
Struct(
OutStruct(
StructPath {
lifetimes: Lifetimes {
indices: [
NonStatic(
Lifetime(
0,
),
],
},
tcx_id: OutStructId(
0,
),
),
],
},
),
tcx_id: OutStructId(
0,
),
},
),
),
),
Expand Down Expand Up @@ -209,17 +207,15 @@ TypeContext {
},
],
output: Infallible(
Some(
OutType(
Slice(
Str(
NonStatic(
Lifetime(
1,
),
OutType(
Slice(
Str(
NonStatic(
Lifetime(
1,
),
UnvalidatedUtf8,
),
UnvalidatedUtf8,
),
),
),
Expand Down
2 changes: 1 addition & 1 deletion core/src/hir/type_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ impl TypeContext {
let enums = ctx.lower_all_enums(ast_enums.into_iter());

match (out_structs, structs, opaques, enums) {
(Some(out_structs), Some(structs), Some(opaques), Some(enums)) => {
(Ok(out_structs), Ok(structs), Ok(opaques), Ok(enums)) => {
assert!(
errors.is_empty(),
"All lowering succeeded but still found error messages: {errors:?}"
Expand Down
24 changes: 11 additions & 13 deletions tool/src/c2/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,26 +133,23 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> {
}

let return_ty: Cow<str> = match method.output {
ReturnType::Infallible(None) => "void".into(),
ReturnType::Infallible(Some(ref ty)) => match ty {
SuccessType::Writeable => {
param_decls.push(("DiplomatWriteable*".into(), "writeable".into()));
"void".into()
}
SuccessType::OutType(o) => self.gen_ty_name(o, false),
&_ => unreachable!("unknown AST/HIR variant"),
},
ReturnType::Infallible(SuccessType::Unit) => "void".into(),
ReturnType::Infallible(SuccessType::Writeable) => {
param_decls.push(("DiplomatWriteable*".into(), "writeable".into()));
"void".into()
}
ReturnType::Infallible(SuccessType::OutType(ref o)) => self.gen_ty_name(o, false),
ReturnType::Fallible(ref ok, ref err) => {
let (ok_type_name, ok_ty) = match ok {
Some(SuccessType::Writeable) => {
SuccessType::Writeable => {
param_decls.push(("DiplomatWriteable*".into(), "writeable".into()));
("void".into(), None)
}
None => ("void".into(), None),
Some(SuccessType::OutType(o)) => {
SuccessType::Unit => ("void".into(), None),
SuccessType::OutType(o) => {
(self.cx.formatter.fmt_type_name_uniquely(o), Some(o))
}
&Some(_) => unreachable!("unknown AST/HIR variant"),
_ => unreachable!("unknown AST/HIR variant"),
};
let err_type_name = match err {
Some(o) => self.cx.formatter.fmt_type_name_uniquely(o),
Expand All @@ -172,6 +169,7 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> {
.insert(result_name.clone(), (ok_ty, err.as_ref()));
result_name.into()
}
_ => unreachable!("unknown AST/HIR variant"),
};

let mut params = String::new();
Expand Down
44 changes: 21 additions & 23 deletions tool/src/cpp2/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> {
);
}

if method.is_writeable() {
if method.output.is_writeable() {
cpp_to_c_params.push("&writeable".into());
}

Expand Down Expand Up @@ -565,18 +565,15 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> {
/// Generates the C++ type name of a return type.
fn gen_cpp_return_type_name(&mut self, result_ty: &ReturnType) -> Cow<'ccx, str> {
match *result_ty {
ReturnType::Infallible(None) => "void".into(),
ReturnType::Infallible(Some(ref ty)) => match ty {
SuccessType::Writeable => self.cx.formatter.fmt_owned_str(),
SuccessType::OutType(o) => self.gen_type_name(o),
&_ => unreachable!("unknown AST/HIR variant"),
},
ReturnType::Infallible(SuccessType::Unit) => "void".into(),
ReturnType::Infallible(SuccessType::Writeable) => self.cx.formatter.fmt_owned_str(),
ReturnType::Infallible(SuccessType::OutType(ref o)) => self.gen_type_name(o),
ReturnType::Fallible(ref ok, ref err) => {
let ok_type_name = match ok {
Some(SuccessType::Writeable) => self.cx.formatter.fmt_owned_str(),
None => "std::monostate".into(),
Some(SuccessType::OutType(o)) => self.gen_type_name(o),
&Some(_) => unreachable!("unknown AST/HIR variant"),
SuccessType::Writeable => self.cx.formatter.fmt_owned_str(),
SuccessType::Unit => "std::monostate".into(),
SuccessType::OutType(o) => self.gen_type_name(o),
_ => unreachable!("unknown AST/HIR variant"),
};
let err_type_name = match err {
Some(o) => self.gen_type_name(o),
Expand All @@ -586,6 +583,7 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> {
format!("diplomat::result<{ok_type_name}, {err_type_name}>").into();
ret
}
_ => unreachable!("unknown AST/HIR variant"),
}
}

Expand Down Expand Up @@ -680,30 +678,30 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> {
var_name: Cow<'a, str>,
) -> Option<Cow<'a, str>> {
match *result_ty {
ReturnType::Infallible(None) => None,
ReturnType::Infallible(Some(SuccessType::Writeable)) => Some("output".into()),
ReturnType::Infallible(Some(SuccessType::OutType(ref out_ty))) => {
ReturnType::Infallible(SuccessType::Unit) => None,
ReturnType::Infallible(SuccessType::Writeable) => Some("output".into()),
ReturnType::Infallible(SuccessType::OutType(ref out_ty)) => {
Some(self.gen_c_to_cpp_for_type(out_ty, var_name))
}
ReturnType::Fallible(ref ok, ref err) => {
let ok_path = format!("{var_name}.ok");
let err_path = format!("{var_name}.err");
let ok_type_name = match ok {
Some(SuccessType::Writeable) => self.cx.formatter.fmt_owned_str(),
None => "std::monostate".into(),
Some(SuccessType::OutType(o)) => self.gen_type_name(o),
&Some(_) => unreachable!("unknown AST/HIR variant"),
SuccessType::Writeable => self.cx.formatter.fmt_owned_str(),
SuccessType::Unit => "std::monostate".into(),
SuccessType::OutType(ref o) => self.gen_type_name(o),
_ => unreachable!("unknown AST/HIR variant"),
};
let err_type_name = match err {
Some(o) => self.gen_type_name(o),
None => "std::monostate".into(),
};
let ok_conversion = match ok {
// Note: the `output` variable is a string initialized in the template
Some(SuccessType::Writeable) => "std::move(output)".into(),
None => "".into(),
Some(SuccessType::OutType(o)) => self.gen_c_to_cpp_for_type(o, ok_path.into()),
&Some(_) => unreachable!("unknown AST/HIR variant"),
SuccessType::Writeable => "std::move(output)".into(),
SuccessType::Unit => "".into(),
SuccessType::OutType(ref o) => self.gen_c_to_cpp_for_type(o, ok_path.into()),
_ => unreachable!("unknown AST/HIR variant"),
};
let err_conversion = match err {
Some(o) => self.gen_c_to_cpp_for_type(o, err_path.into()),
Expand All @@ -713,7 +711,7 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> {
format!("{var_name}.is_ok ? diplomat::result<{ok_type_name}, {err_type_name}>(diplomat::Ok<{ok_type_name}>({ok_conversion})) : diplomat::result<{ok_type_name}, {err_type_name}>(diplomat::Err<{err_type_name}>({err_conversion}))").into()
)
}
ReturnType::Infallible(Some(_)) => unreachable!("unknown AST/HIR variant"),
_ => unreachable!("unknown AST/HIR variant"),
}
}
}
Loading
Loading