Skip to content

Commit dd7365a

Browse files
committed
Partial merge of hsutter#106: Allow as literal (not-really-)narrowing, pull unsafe_narrow into cpp2::
1 parent c5ce444 commit dd7365a

File tree

2 files changed

+79
-20
lines changed

2 files changed

+79
-20
lines changed

include/cpp2util.h

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -701,11 +701,63 @@ inline constexpr auto is( auto const& x, auto const& value ) -> bool
701701
//-------------------------------------------------------------------------------------------------------------
702702
// Built-in as
703703
//
704+
705+
// Helpers
706+
//
707+
template <typename... Ts>
708+
inline constexpr auto program_violates_type_safety_guarantee = sizeof...(Ts) < 0;
709+
704710
template< typename C >
705711
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+
// );
706716
return nonesuch;
707717
}
708718

719+
// For literals we can check for safe 'narrowing' at a compile time
720+
template< typename C, auto x >
721+
inline constexpr bool is_castable_v =
722+
std::is_arithmetic_v<C> &&
723+
std::is_arithmetic_v<CPP2_TYPEOF(x)> &&
724+
!(static_cast<CPP2_TYPEOF(x)>(static_cast<C>(x)) != x ||
725+
(
726+
(std::is_signed_v<C> != std::is_signed_v<CPP2_TYPEOF(x)>) &&
727+
((static_cast<C>(x) < C{}) != (x < CPP2_TYPEOF(x){}))
728+
)
729+
);
730+
731+
// As
732+
//
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+
}
745+
}
746+
747+
template< typename C, auto x >
748+
requires (std::is_arithmetic_v<C> && std::is_arithmetic_v<CPP2_TYPEOF(x)>)
749+
inline constexpr auto as() -> auto
750+
{
751+
if constexpr ( is_castable_v<C, x> ) {
752+
return static_cast<C>(CPP2_TYPEOF(x)(x));
753+
} else {
754+
static_assert(
755+
program_violates_type_safety_guarantee<C, CPP2_TYPEOF(x)>,
756+
"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"
757+
);
758+
}
759+
}
760+
709761
template< typename C, typename X >
710762
requires std::is_same_v<C, X>
711763
auto as( X const& x ) -> decltype(auto) {
@@ -1189,33 +1241,22 @@ inline auto fopen( const char* filename, const char* mode ) {
11891241
// with cpp2::fopen as a starting example.
11901242

11911243

1192-
}
1193-
1194-
1195-
using cpp2::cpp2_new;
1196-
1197-
11981244
//-----------------------------------------------------------------------
11991245
//
1200-
// A partial implementation of GSL features Cpp2 relies on,
1201-
// to keep this a standalone header without non-std dependencies
1202-
//
1203-
//-----------------------------------------------------------------------
1246+
// An implementation of GSL's narrow_cast with a clearly 'unsafe' name
12041247
//
1205-
namespace gsl {
1206-
12071248
//-----------------------------------------------------------------------
12081249
//
1209-
// An implementation of GSL's narrow_cast
1210-
//
1211-
//-----------------------------------------------------------------------
1212-
//
1213-
template<typename To, typename From>
1214-
constexpr auto narrow_cast(From&& from) noexcept -> To
1250+
template <typename C, typename X>
1251+
auto unsafe_narrow( X&& x ) noexcept -> decltype(auto)
12151252
{
1216-
return static_cast<To>(std::forward<From>(from));
1253+
return static_cast<C>(CPP2_FORWARD(x));
12171254
}
12181255

12191256
}
12201257

1258+
1259+
using cpp2::cpp2_new;
1260+
1261+
12211262
#endif

source/cppfront.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2011,6 +2011,19 @@ class cppfront
20112011
std::string suffix = {};
20122012

20132013
auto wildcard_found = false;
2014+
bool as_on_literal = false;
2015+
2016+
assert(
2017+
n.expr &&
2018+
n.expr->get_postfix_expression_node() &&
2019+
n.expr->get_postfix_expression_node()->expr
2020+
);
2021+
if (auto t = n.expr->get_postfix_expression_node()->expr->get_token();
2022+
t && is_literal(t->type()) && t->type() != lexeme::StringLiteral && t->type() != lexeme::FloatLiteral
2023+
&& std::ssize(n.ops) > 0 && *n.ops[0].op == "as"
2024+
) {
2025+
as_on_literal = true;
2026+
}
20142027

20152028
for (auto i = n.ops.rbegin(); i != n.ops.rend(); ++i)
20162029
{
@@ -2046,11 +2059,16 @@ class cppfront
20462059
}
20472060
}
20482061

2062+
if (as_on_literal) {
2063+
auto last_pos = prefix.rfind('>'); assert(last_pos != prefix.npos);
2064+
prefix.insert(last_pos, ", " + print_to_string(*n.expr));
2065+
}
2066+
20492067
printer.print_cpp2(prefix, n.position());
20502068
if (wildcard_found) {
20512069
printer.print_cpp2("true", n.position());
20522070
}
2053-
else {
2071+
else if(!as_on_literal) {
20542072
emit(*n.expr);
20552073
}
20562074
printer.print_cpp2(suffix, n.position());

0 commit comments

Comments
 (0)