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

polyfill: Add helper for defining error types. #2219

Merged
merged 2 commits into from
Jan 12, 2025
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
14 changes: 3 additions & 11 deletions src/aead/overlapping/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#![cfg_attr(not(test), allow(dead_code))]

pub use self::len_mismatch_error::LenMismatchError;
use super::Overlapping;
use core::array::TryFromSliceError;

Expand Down Expand Up @@ -58,15 +59,6 @@ impl<T, const N: usize> Array<'_, T, N> {
}
}

pub struct LenMismatchError {
#[allow(dead_code)]
len: usize,
}

impl LenMismatchError {
#[cold]
#[inline(never)]
fn new(len: usize) -> Self {
Self { len }
}
cold_exhaustive_error! {
struct len_mismatch_error::LenMismatchError { len: usize }
}
11 changes: 3 additions & 8 deletions src/aead/overlapping/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

pub use self::index_error::IndexError;
use super::{Array, LenMismatchError};
use core::{mem, ops::RangeFrom};

Expand Down Expand Up @@ -130,12 +131,6 @@ impl<T> Overlapping<'_, T> {
}
}

pub struct IndexError(#[allow(dead_code)] usize);

impl IndexError {
#[cold]
#[inline(never)]
fn new(index: usize) -> Self {
Self(index)
}
cold_exhaustive_error! {
struct index_error::IndexError { index: usize }
}
23 changes: 9 additions & 14 deletions src/digest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
};
use core::num::Wrapping;

pub(crate) use self::finish_error::FinishError;

mod dynstate;
mod sha1;
mod sha2;
Expand Down Expand Up @@ -146,27 +148,20 @@

pub(crate) type InputTooLongError = error::InputTooLongError<u64>;

pub(crate) enum FinishError {
#[allow(dead_code)]
InputTooLong(InputTooLongError),
#[allow(dead_code)]
PendingNotAPartialBlock(usize),
cold_exhaustive_error! {
enum finish_error::FinishError {
input_too_long => InputTooLong(InputTooLongError),
pending_not_a_partial_block_inner => PendingNotAPartialBlock(usize),
}
}

impl FinishError {
#[cold]
#[inline(never)]
fn input_too_long(source: InputTooLongError) -> Self {
Self::InputTooLong(source)
}

// unreachable
#[cold]
#[inline(never)]
fn pending_not_a_partial_block(padding: Option<&[u8]>) -> Self {
match padding {
None => Self::PendingNotAPartialBlock(0),
Some(padding) => Self::PendingNotAPartialBlock(padding.len()),
None => Self::pending_not_a_partial_block_inner(0),
Some(padding) => Self::pending_not_a_partial_block_inner(padding.len()),

Check warning on line 164 in src/digest.rs

View check run for this annotation

Codecov / codecov/patch

src/digest.rs#L163-L164

Added lines #L163 - L164 were not covered by tests
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/polyfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ pub const fn usize_from_u64_saturated(x: u64) -> usize {
}
}

#[macro_use]
mod cold_error;

mod array_flat_map;
mod array_split_map;

Expand Down
89 changes: 89 additions & 0 deletions src/polyfill/cold_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2024 Brian Smith.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

/// Reduces boilerplate for defining error types where we want the compiler to
/// optimize for the non-error path by assuming constructing an error is
/// unlikely/cold code.
///
/// WARNING: Every struct/variant must contain some *non-constant* value so
/// that the "invariant code" pass of the compiler doesn't recognize the
/// constructor as being "invariant code" and optimizing it away;
/// although such optimization would be nice to take advantage of, it
/// seems to lose the `#[cold]` attribute.
///
/// Constructor functions ar marked `pub(super)` to ensure that instances can
/// only be constructed from within the enclosing module (and its submodules).
///
/// XXX: #[inline(never)] is required to avoid the (MIR?) optimizer inlining
/// away the function call and losing the `#[cold]` attribute in the process.
/// We'd otherwise maybe prefer all constructors to be inline.
///
/// The type is defined in its own submodule `#mod_name` to hide the
/// variant/struct constructor, ensuring instances are only constructed
/// through the generated `$constructor` functions. The constructor methods
/// work around the lack of the ability to mark an enum variant `#[cold]` and
/// `#[inline(never)]`.
macro_rules! cold_exhaustive_error {
// struct
{
struct $mod_name:ident::$Error:ident {
$field:ident: $ValueType:ty
}
} => {
mod $mod_name {
#[allow(unused_imports)]
use super::*; // So `$ValueType` is in scope.

pub struct $Error { #[allow(dead_code)] $field: $ValueType }

impl $Error {
#[cold]
#[inline(never)]
pub(super) fn new($field: $ValueType) -> Self {
Self { $field }
}

Check warning on line 55 in src/polyfill/cold_error.rs

View check run for this annotation

Codecov / codecov/patch

src/polyfill/cold_error.rs#L53-L55

Added lines #L53 - L55 were not covered by tests
}
}
};

// enum
{
enum $mod_name:ident::$Error:ident {
$(
$constructor:ident => $Variant:ident($ValueType:ty),
)+
}
} => {
mod $mod_name {
#[allow(unused_imports)]
use super::*; // So `$ValueType` is in scope.

pub enum $Error {
$(
$Variant(#[allow(dead_code)] $ValueType)
),+
}

impl $Error {
$(
#[cold]
#[inline(never)]
pub(super) fn $constructor(value: $ValueType) -> Self {
Self::$Variant(value)
}
)+
}
}
};
}