Skip to content

[SUGGESTION][FIX] Add safety check support for cpp1 pointers and deduced from other cpp2 declarations #96

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

Closed
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
Binary file added a.out
Binary file not shown.
130 changes: 130 additions & 0 deletions include/cpp2util.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@
#include <cstddef>
#include <utility>
#include <cstdio>
#include <span>

#if defined(CPP2_USE_SOURCE_LOCATION)
#include <source_location>
Expand Down Expand Up @@ -498,6 +499,135 @@ class out {
}(PARAM1)
//--------------------------------------------------------------------

//-----------------------------------------------------------------------
//
// cpp2::safety_check() ensures that cpp1 pointers are also covered by safetychecks
//
//-----------------------------------------------------------------------
//
template <typename... Ts>
inline constexpr auto program_violates_lifetime_safety_guarantee = sizeof...(Ts) < 0;

template <typename T>
requires std::is_pointer_v<T>
class safetychecked_pointer {
T ptr;
public:

constexpr safetychecked_pointer(T ptr) : ptr{ptr} {}

constexpr operator T&() noexcept { return ptr; }

template <typename... Ts> void operator+ () const {static_assert(program_violates_lifetime_safety_guarantee<Ts...>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename... Ts> void operator- () const {static_assert(program_violates_lifetime_safety_guarantee<Ts...>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename X> void operator+ (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename X> void operator- (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename X> void operator* (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename X> void operator/ (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename X> void operator% (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X> void operator^ (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X> void operator& (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X> void operator| (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}

template <typename... Ts> void operator++ (Ts...) const {static_assert(program_violates_lifetime_safety_guarantee<Ts...>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename... Ts> void operator-- (Ts...) const {static_assert(program_violates_lifetime_safety_guarantee<Ts...>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename... Ts> void operator[] (Ts...) const {static_assert(program_violates_lifetime_safety_guarantee<Ts...>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename X> void operator+= (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename X> void operator-= (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename X> void operator*= (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename X> void operator/= (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}

template <typename... Ts> void operator~ () const {static_assert(program_violates_lifetime_safety_guarantee<Ts...>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X > void operator%= (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X > void operator^= (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X > void operator&= (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X > void operator|= (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X > void operator<<=(X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X > void operator>>=(X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X > void operator<< (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X > void operator>> (X) const {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}

template <typename X > friend void operator+ (X, const safetychecked_pointer&) {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename X > friend void operator- (X, const safetychecked_pointer&) {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename X > friend void operator* (X, const safetychecked_pointer&) {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename X > friend void operator/ (X, const safetychecked_pointer&) {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer arithmetic is illegal - use std::span or gsl::span instead");}
template <typename X > friend void operator% (X, const safetychecked_pointer&) {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X > friend void operator^ (X, const safetychecked_pointer&) {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X > friend void operator& (X, const safetychecked_pointer&) {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}
template <typename X > friend void operator| (X, const safetychecked_pointer&) {static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first");}


template <typename X>
requires (std::is_same_v<T,X> || std::is_base_of_v<T, X>)
constexpr safetychecked_pointer& operator=(X lhs) noexcept {
ptr = lhs;
return *this;
}

template <typename X>
requires std::is_same_v<std::nullptr_t,X>
constexpr void operator=(X lhs) noexcept { static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer assignment from null is illegal"); }

template <typename X>
requires std::is_integral_v<X>
constexpr void operator=(X lhs) noexcept { static_assert(program_violates_lifetime_safety_guarantee<X>, "pointer assignment from integer is illegal"); }

bool operator!() const { return !ptr; }

constexpr safetychecked_pointer<T*> operator&() noexcept { return &ptr; }

constexpr auto operator*() noexcept {
if constexpr (std::is_pointer_v<CPP2_TYPEOF(*ptr)>) {
return safetychecked_pointer<CPP2_TYPEOF(*ptr)>(*ptr);
} else {
return *ptr;
}
}

constexpr T operator->() const noexcept { return ptr; }
};

template <typename X>
requires ( !std::is_pointer_v<std::remove_cvref_t<X>>
&& std::is_copy_constructible_v<X> )
inline constexpr auto safety_check(X const& x) {
return x;
}

template <typename R, typename... Args>
inline constexpr auto safety_check(R (&x)(Args...)) {
return x;
}

template <typename X>
requires std::is_rvalue_reference_v<X>
inline constexpr decltype(auto) safety_check(X&& x) {
return std::forward<X>(x);
}

template <typename X>
requires (std::is_pointer_v<std::remove_cvref_t<X>> && !std::is_bounded_array_v<X>)
inline constexpr auto safety_check(X const& x) {
return safetychecked_pointer(x);
}

template <typename X>
requires (!std::is_pointer_v<std::remove_cvref_t<X>> && !std::is_function_v<X> && !std::is_bounded_array_v<X>)
inline constexpr auto& safety_check(X& x) {
return x;
}

template <typename X>
requires (!std::is_copy_constructible_v<X>)
inline constexpr auto safety_check(X&& x) {
return std::forward<X>(x);
}

template <typename X>
requires std::is_bounded_array_v<X>
inline constexpr auto safety_check(X const& x) {
return std::span(x);
}

//-----------------------------------------------------------------------
//
Expand Down
31 changes: 31 additions & 0 deletions regression-tests/pure2-deduced-pointers.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
fun: (inout i:int) -> *int = {
return i&;
}

main: (argc : int, argv : **char) -> int = {
a: int = 2;
pa: *int = a&;
ppa: **int = pa&;

pa = 0; // caught

pa2:= ppa*;
pa2 = 0; // caught

pa3 := a&;
pa3 = 0; // caught
pa3 += 2; // caught

ppa2 := pa2&;
pa4 := ppa2*;
pa4 = 0; // caught

pppa := ppa&;
pa5 := pppa**;
pa5 = 0; // caught

ff := fun(a);
ff = 0; // caught?

return a*pa**ppa**; // 8
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ auto null_from_cpp1() -> int* { return nullptr; }

auto try_pointer_stuff() -> void;
#line 21 "mixed-lifetime-safety-and-null-contracts.cpp2"
auto call_my_framework(cpp2::in<const char *> msg) -> void;
auto call_my_framework(cpp2::in<const char*> msg) -> void;

//=== Cpp2 definitions ==========================================================

Expand All @@ -29,12 +29,12 @@ auto call_my_framework(cpp2::in<const char *> msg) -> void;
#line 14 "mixed-lifetime-safety-and-null-contracts.cpp2"

auto try_pointer_stuff() -> void{
int* p { null_from_cpp1() };
int * p { null_from_cpp1() };
*p = 42; // deliberate null dereference
// to show -n
}

auto call_my_framework(cpp2::in<const char *> msg) -> void {
auto call_my_framework(cpp2::in<const char*> msg) -> void {
std::cout
<< "sending error to my framework... ["
<< msg << "]\n"; }
2 changes: 1 addition & 1 deletion regression-tests/test-results/mixed-type-safety-1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ auto print(cpp2::in<std::string> msg, cpp2::in<bool> b) -> void
print( "1 is int? ", cpp2::is<int>(1));

auto c { cpp2_new<Circle>() }; // safe by construction
Shape* s { CPP2_UFCS_0(get, c) }; // safe by Lifetime
Shape * s { CPP2_UFCS_0(get, c) }; // safe by Lifetime
print("\ns* is Shape? ", cpp2::is<Shape>(*s));
print( "s* is Circle? ", cpp2::is<Circle>(*s));
print( "s* is Square? ", cpp2::is<Square>(*s));
Expand Down
10 changes: 10 additions & 0 deletions regression-tests/test-results/pure2-deduced-pointers.cpp2.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pure2-deduced-pointers.cpp2...
pure2-deduced-pointers.cpp2(10,8): error: = - pointer assignment from null or integer is illegal
pure2-deduced-pointers.cpp2(13,9): error: = - pointer assignment from null or integer is illegal
pure2-deduced-pointers.cpp2(16,9): error: = - pointer assignment from null or integer is illegal
pure2-deduced-pointers.cpp2(17,9): error: += - pointer assignment from null or integer is illegal
pure2-deduced-pointers.cpp2(21,9): error: = - pointer assignment from null or integer is illegal
pure2-deduced-pointers.cpp2(25,9): error: = - pointer assignment from null or integer is illegal
pure2-deduced-pointers.cpp2(28,8): error: = - pointer assignment from null or integer is illegal
==> program violates lifetime safety guarantee - see previous errors

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ auto println(auto const& x, auto const& len) -> void;
std::span view { vec };

for ( auto&& cpp2_range = view; auto& str : cpp2_range ) {
auto len { decorate(str) };
auto len { cpp2::safety_check(decorate(str)) };
println(str, len);
}
}
Expand Down
2 changes: 1 addition & 1 deletion regression-tests/test-results/pure2-stdio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//
[[nodiscard]] auto main() -> int{
std::string s { "Fred" };
auto myfile { fopen("xyzzy", "w") };
auto myfile { cpp2::safety_check(fopen("xyzzy", "w")) };
CPP2_UFCS(fprintf, myfile, "Hello %s with UFCS!", CPP2_UFCS_0(c_str, s));
CPP2_UFCS_0(fclose, myfile);
}
Loading