Skip to content

Commit ede2df2

Browse files
committed
More partial merge of #106: Support signedness-only "narrowing" as contract precondition
Signed/unsigned conversions to a not-smaller type are handled as a precondition, and trying to cast from a source value that is in the half of the value space that isn't representable in the target type is flagged as a `Type` safety contract violation
1 parent aa3dadc commit ede2df2

File tree

1 file changed

+34
-33
lines changed

1 file changed

+34
-33
lines changed

include/cpp2util.h

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -593,13 +593,6 @@ class out {
593593
// Built-in is
594594
//
595595

596-
// For use when returning "no such thing", such as
597-
// when customizing is/as for std::variant
598-
struct nonesuch_ {
599-
auto operator==(auto const&) -> bool { return false; }
600-
};
601-
static nonesuch_ nonesuch;
602-
603596
// For designating "holds no value" -- used only with is, not as
604597
// TODO: Does this really warrant a new synonym? Perhaps "is void" is enough
605598
using empty = void;
@@ -702,25 +695,21 @@ inline constexpr auto is( auto const& x, auto const& value ) -> bool
702695
// Built-in as
703696
//
704697

705-
// Helpers
706-
//
698+
// For use when returning "no such thing", such as
699+
// when customizing "as" for std::variant
700+
struct nonesuch_ {
701+
auto operator==(auto const&) -> bool { return false; }
702+
};
703+
static nonesuch_ nonesuch;
704+
707705
template <typename... Ts>
708706
inline constexpr auto program_violates_type_safety_guarantee = sizeof...(Ts) < 0;
709707

710-
template< typename C >
711-
auto as(auto const&) -> auto {
712-
//static_assert(
713-
// program_violates_type_safety_guarantee<C, CPP2_TYPEOF(x)>,
714-
// "No safe 'as' cast available - if this is narrowing and you're sure the conversion is safe, consider using `unsafe_narrow<T>()` to force the conversion"
715-
// );
716-
return nonesuch;
717-
}
718-
719-
// For literals we can check for safe 'narrowing' at a compile time
708+
// For literals we can check for safe 'narrowing' at a compile time (e.g., 1 as size_t)
720709
template< typename C, auto x >
721710
inline constexpr bool is_castable_v =
722-
std::is_arithmetic_v<C> &&
723-
std::is_arithmetic_v<CPP2_TYPEOF(x)> &&
711+
std::is_integral_v<C> &&
712+
std::is_integral_v<CPP2_TYPEOF(x)> &&
724713
!(static_cast<CPP2_TYPEOF(x)>(static_cast<C>(x)) != x ||
725714
(
726715
(std::is_signed_v<C> != std::is_signed_v<CPP2_TYPEOF(x)>) &&
@@ -730,18 +719,10 @@ inline constexpr bool is_castable_v =
730719

731720
// As
732721
//
733-
template< typename C, auto x >
734-
inline constexpr auto as() -> auto
735-
{
736-
if constexpr ( is_castable_v<C, x> ) {
737-
return static_cast<C>(CPP2_TYPEOF(x)(x));
738-
}
739-
else {
740-
static_assert(
741-
program_violates_type_safety_guarantee<C, CPP2_TYPEOF(x)>,
742-
"No safe 'as' cast available - if this is narrowing and you're sure the conversion is safe, consider using `unsafe_narrow<T>()` to force the conversion"
743-
);
744-
}
722+
723+
template< typename C >
724+
auto as(auto const&) -> auto {
725+
return nonesuch;
745726
}
746727

747728
template< typename C, auto x >
@@ -758,6 +739,26 @@ inline constexpr auto as() -> auto
758739
}
759740
}
760741

742+
// Signed/unsigned conversions to a not-smaller type are handled as a precondition,
743+
// and trying to cast from a value that is in the half of the value space that isn't
744+
// representable in the target type C is flagged as a Type safety contract violation
745+
template< typename C >
746+
inline constexpr auto as(auto const& x) -> auto
747+
requires (
748+
std::is_integral_v<C> &&
749+
std::is_integral_v<CPP2_TYPEOF(x)> &&
750+
std::is_signed_v<CPP2_TYPEOF(x)> != std::is_signed_v<C> &&
751+
sizeof(CPP2_TYPEOF(x)) <= sizeof(C)
752+
)
753+
{
754+
const C c = static_cast<C>(x);
755+
Type.expects( // precondition check: must be round-trippable => not lossy
756+
static_cast<CPP2_TYPEOF(x)>(c) == x && (c < C{}) == (x < CPP2_TYPEOF(x){}),
757+
"dynamic lossy narrowing conversion attempt detected" CPP2_SOURCE_LOCATION_ARG
758+
);
759+
return c;
760+
}
761+
761762
template< typename C, typename X >
762763
requires std::is_same_v<C, X>
763764
auto as( X const& x ) -> decltype(auto) {

0 commit comments

Comments
 (0)