Skip to content

Commit 2b61ccf

Browse files
committed
Don't allow null pointers
Don't allow 'NULL` Don't allow initializing or assigning a pointer with null or an integer literal (`nullptr`, `0`, etc.) Start to put in `is` and `as` including for `std::variant`
1 parent c5d2741 commit 2b61ccf

File tree

5 files changed

+267
-69
lines changed

5 files changed

+267
-69
lines changed

cpp2util.h

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import std.threading;
3939
#include <string_view>
4040
#include <iostream>
4141
#include <cassert>
42+
#include <variant>
4243
#endif
4344

4445

@@ -299,6 +300,96 @@ auto assert_not_null(auto&& p CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> auto&&
299300
}(PARAM1)
300301

301302

303+
304+
/*
305+
//-----------------------------------------------------------------------
306+
//
307+
// is and as
308+
//
309+
//-----------------------------------------------------------------------
310+
//
311+
#include <cstddef>
312+
#include <iostream>
313+
#include <cassert>
314+
315+
#define typeof(x) std::remove_cvref_t<decltype(x)>
316+
317+
318+
//-------------------------------------------------------------------------------------------------------------
319+
// Built-in is
320+
//
321+
template<typename T, typename X>
322+
auto is( X const& x ) {
323+
return false;
324+
}
325+
326+
template<typename T, typename X>
327+
requires std::is_same_v<T, X>
328+
auto is( X const& x ) {
329+
return true;
330+
}
331+
332+
333+
334+
//-------------------------------------------------------------------------------------------------------------
335+
// variant is and as
336+
//
337+
template<typename... Ts>
338+
constexpr auto operator_is( std::variant<Ts...> const& x ) {
339+
return x.index();
340+
}
341+
template<size_t I, typename... Ts>
342+
constexpr auto operator_as( std::variant<Ts...> const& x ) -> auto&& {
343+
return std::get<I>( x );
344+
}
345+
346+
template<typename T, typename... Ts>
347+
auto is( std::variant<Ts...> const& x ) {
348+
if constexpr (std::is_same_v< typeof(operator_as<0>(x)), T >) if (x.index() == 0) return true;
349+
if constexpr (std::is_same_v< typeof(operator_as<1>(x)), T >) if (x.index() == 1) return true;
350+
if constexpr (std::is_same_v< typeof(operator_as<2>(x)), T >) if (x.index() == 2) return true;
351+
return false;
352+
}
353+
354+
template<typename T, typename... Ts>
355+
auto as( std::variant<Ts...> const& x ) {
356+
if constexpr (std::is_same_v< typeof(operator_as<0>(x)), T >) if (x.index() == 0) return operator_as<0>(x);
357+
if constexpr (std::is_same_v< typeof(operator_as<1>(x)), T >) if (x.index() == 1) return operator_as<1>(x);
358+
if constexpr (std::is_same_v< typeof(operator_as<2>(x)), T >) if (x.index() == 2) return operator_as<2>(x);
359+
throw std::bad_variant_access();
360+
}
361+
362+
//=============================================================================================================
363+
364+
int main() {
365+
std::cout << "1 is int == " << is<int>(1) << "\n";
366+
std::cout << "1.1 is int == " << is<int>(1.1) << "\n";
367+
368+
369+
std::variant<int, int, float> v;
370+
371+
v.emplace<0>(42);
372+
std::cout << "v.index() == " << v.index() << "\n";
373+
std::cout << "v is int = " << (is<int>(v) ? "true" : "false") << "\n";
374+
std::cout << " -> v as int == " << as<int>(v) << "\n";
375+
std::cout << "v is float = " << (is<float>(v) ? "true" : "false") << "\n\n";
376+
377+
v.emplace<1>(84);
378+
std::cout << "v.index() == " << v.index() << "\n";
379+
std::cout << "v is int = " << (is<int>(v) ? "true" : "false") << "\n";
380+
std::cout << " -> v as int == " << as<int>(v) << "\n";
381+
std::cout << "v is float = " << (is<float>(v) ? "true" : "false") << "\n\n";
382+
383+
v.emplace<2>(3.14159);
384+
std::cout << "v.index() == " << v.index() << "\n";
385+
std::cout << "v is int = " << (is<int>(v) ? "true" : "false") << "\n";
386+
std::cout << "v is float = " << (is<float>(v) ? "true" : "false") << "\n";
387+
std::cout << " -> v as float == " << as<float>(v) << "\n";
388+
}
389+
390+
*/
391+
392+
302393
}
303394

304395
#endif

cppfront.cpp

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ class cppfront
555555

556556
bool source_loaded = true;
557557
bool last_postfix_expr_was_pointer = false;
558-
bool bounds_safety_violation = false;
558+
bool violates_bounds_safety = false;
559559
bool suppress_move_from_last_use = false;
560560

561561
// For lowering
@@ -643,8 +643,14 @@ class cppfront
643643
catch (std::runtime_error& e) {
644644
errors.emplace_back(
645645
source_position(-1, -1),
646-
"unexpected text or end-of-file - see previous errors\n"
646+
e.what()
647647
);
648+
if (violates_lifetime_safety) {
649+
errors.emplace_back(
650+
source_position(-1, -1),
651+
"program violates lifetime safety guarantee - see previous errors"
652+
);
653+
}
648654
}
649655
}
650656
}
@@ -1142,12 +1148,6 @@ class cppfront
11421148
assert(n.expr);
11431149
last_postfix_expr_was_pointer = false;
11441150

1145-
// Simple case: If there are no .ops, just emit the expression
1146-
if (n.ops.empty()) {
1147-
emit(*n.expr);
1148-
return;
1149-
}
1150-
11511151
// Check that this isn't pointer arithmentic
11521152
// (initial partial implementation)
11531153
if (n.expr->expr.index() == primary_expression_node::id_expression)
@@ -1163,23 +1163,32 @@ class cppfront
11631163
// We don't recognize pointer types that are deduced, multi-level, or from Cpp1
11641164
if (decl && decl->declaration && decl->declaration->pointer_declarator) {
11651165
last_postfix_expr_was_pointer = true;
1166-
if (n.ops.front().op->type() == lexeme::PlusPlus || n.ops.front().op->type() == lexeme::MinusMinus) {
1167-
errors.emplace_back(
1168-
n.ops.front().op->position(),
1169-
n.ops.front().op->to_string(true) + " - pointer arithmetic is illegal - use std::span or gsl::span instead"
1170-
);
1171-
bounds_safety_violation = true;
1172-
}
1173-
else if (n.ops.front().op->type() == lexeme::Tilde) {
1174-
errors.emplace_back(
1175-
n.ops.front().op->position(),
1176-
n.ops.front().op->to_string(true) + " - pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first"
1177-
);
1166+
if (!n.ops.empty())
1167+
{
1168+
if (n.ops.front().op->type() == lexeme::PlusPlus || n.ops.front().op->type() == lexeme::MinusMinus) {
1169+
errors.emplace_back(
1170+
n.ops.front().op->position(),
1171+
n.ops.front().op->to_string(true) + " - pointer arithmetic is illegal - use std::span or gsl::span instead"
1172+
);
1173+
violates_bounds_safety = true;
1174+
}
1175+
else if (n.ops.front().op->type() == lexeme::Tilde) {
1176+
errors.emplace_back(
1177+
n.ops.front().op->position(),
1178+
n.ops.front().op->to_string(true) + " - pointer bitwise manipulation is illegal - use std::bit_cast to convert to raw bytes first"
1179+
);
1180+
}
11781181
}
11791182
}
11801183
}
11811184
}
11821185

1186+
// Simple case: If there are no .ops, just emit the expression
1187+
if (n.ops.empty()) {
1188+
emit(*n.expr);
1189+
return;
1190+
}
1191+
11831192
// Check to see if it's just a function call,
11841193
// and if so use this path to convert it to UFCS
11851194
if (std::ssize(n.ops) == 1 &&
@@ -1351,18 +1360,6 @@ class cppfront
13511360
{
13521361
assert(n.expr);
13531362

1354-
// Check that this isn't pointer arithmentic
1355-
// (initial partial implementation)
1356-
if (!n.terms.empty() && (*n.terms.front().op == "+" || *n.terms.front().op == "-")) {
1357-
if (last_postfix_expr_was_pointer) {
1358-
errors.emplace_back(
1359-
n.terms.front().op->position(),
1360-
n.terms.front().op->to_string(true) + " - pointer arithmetic is illegal - use std::span or gsl::span instead"
1361-
);
1362-
bounds_safety_violation = true;
1363-
}
1364-
}
1365-
13661363
// Handle is/as expressions
13671364
// For now, hack in just a single 'as'-cast
13681365
// TODO: Generalize
@@ -1393,6 +1390,36 @@ class cppfront
13931390
emit(*n.expr);
13941391
suppress_move_from_last_use = false;
13951392

1393+
// Check that this isn't an illegal pointer operation
1394+
// (initial partial implementation)
1395+
if (!n.terms.empty() && last_postfix_expr_was_pointer)
1396+
{
1397+
auto rhs_post = n.get_second_postfix_expression_node();
1398+
assert(rhs_post && rhs_post->expr);
1399+
auto rhs_tok = rhs_post->expr->get_token();
1400+
if (is_assignment_operator(n.terms.front().op->type()) && rhs_tok &&
1401+
(*rhs_tok == "nullptr" || is_digit(((std::string_view)*rhs_tok)[0]))
1402+
)
1403+
{
1404+
errors.emplace_back(
1405+
n.terms.front().op->position(),
1406+
n.terms.front().op->to_string(true) + " - pointer assignment from null or integer is illegal"
1407+
);
1408+
violates_lifetime_safety = true;
1409+
}
1410+
else if (
1411+
*n.terms.front().op == "+" || *n.terms.front().op == "+=" ||
1412+
*n.terms.front().op == "-" || *n.terms.front().op == "-="
1413+
)
1414+
{
1415+
errors.emplace_back(
1416+
n.terms.front().op->position(),
1417+
n.terms.front().op->to_string(true) + " - pointer arithmetic is illegal - use std::span or gsl::span instead"
1418+
);
1419+
violates_bounds_safety = true;
1420+
}
1421+
}
1422+
13961423
for (auto const& x : n.terms) {
13971424
assert(x.op);
13981425
assert(x.expr);
@@ -1936,7 +1963,10 @@ class cppfront
19361963
for (auto&& error : errors) {
19371964
error.print(std::cerr, strip_path(sourcefile));
19381965
}
1939-
if (bounds_safety_violation) {
1966+
if (violates_lifetime_safety) {
1967+
std::cerr << "program violates lifetime safety guarantee - see previous errors\n";
1968+
}
1969+
if (violates_bounds_safety) {
19401970
std::cerr << "program violates bounds safety guarantee - see previous errors\n";
19411971
}
19421972
}

lex.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,12 @@ auto lex_line(
793793
//
794794
else if (auto j = starts_with_identifier({&line[i], std::size(line)-i})) {
795795
store(j, lexeme::Identifier);
796+
if (tokens.back() == "NULL") {
797+
errors.emplace_back(
798+
source_position(lineno, i),
799+
"'NULL' is not supported in Cpp2 - for a local pointer variable, leave it uninitialized instead, and set it to a non-null value when you have one"
800+
);
801+
}
796802
if (tokens.back() == "union") {
797803
errors.emplace_back(
798804
source_position(lineno, i),

0 commit comments

Comments
 (0)