Skip to content

[SUGGESTION][FIX] Add pointer deduce type support #94

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
24 changes: 24 additions & 0 deletions regression-tests/pure2-deduced-pointers.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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

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
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pure2-deduced-pointers.cpp2...
pure2-deduced-pointers.cpp2(6,8): error: = - pointer assignment from null or integer is illegal
pure2-deduced-pointers.cpp2(9,9): error: = - pointer assignment from null or integer is illegal
pure2-deduced-pointers.cpp2(12,9): 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(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
==> program violates lifetime safety guarantee - see previous errors

53 changes: 43 additions & 10 deletions source/cppfront.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,10 @@ class cppfront
assert(n.identifier);
emit(*n.identifier);

for (auto _ : n.pointer_declarators) {
printer.print_cpp2("*", n.position());
}

if (!n.template_args.empty()) {
printer.print_cpp2("<", n.open_angle);
auto first = true;
Expand Down Expand Up @@ -1551,6 +1555,32 @@ class cppfront
}


//-----------------------------------------------------------------------
//
auto is_it_pointer_declaration(declaration_sym const* decl, int deref_cnt = 0, int addr_cnt = 0) -> bool {
if (!decl || !decl->declaration)
return false;
if (addr_cnt>deref_cnt)
return true;

if (decl->declaration->dereference) {
auto deref = sema.get_local_declaration_of(*decl->declaration->dereference);
return is_it_pointer_declaration(deref, deref_cnt+decl->declaration->dereference_cnt, addr_cnt);
} else if (decl->declaration->address_of) {
auto addr = sema.get_local_declaration_of(*decl->declaration->address_of);
return is_it_pointer_declaration(addr, deref_cnt, addr_cnt+1);
}

int pointer_declarators_cnt = 0;
if (auto* obj_id_expr = std::get_if<declaration_node::object>(&decl->declaration->type)) {
if (auto* unqual = std::get_if<id_expression_node::unqualified>(&(*obj_id_expr)->id)){
pointer_declarators_cnt = std::ssize((*unqual)->pointer_declarators);
}
}

return (pointer_declarators_cnt + addr_cnt - deref_cnt) > 0;
};

//-----------------------------------------------------------------------
//
auto emit(postfix_expression_node& n, bool for_lambda_capture = false) -> void
Expand All @@ -1572,9 +1602,19 @@ class cppfront
auto& unqual = std::get<id_expression_node::unqualified>(id->id);
assert(unqual);
auto decl = sema.get_local_declaration_of(*unqual->identifier);
// TODO: Generalize this -- for now we detect only cases of the form "p: *int = ...;"
// We don't recognize pointer types that are deduced, multi-level, or from Cpp1
if (decl && decl->declaration && decl->declaration->pointer_declarator) {

bool is_pointer = false;
if (decl && decl->declaration) {
if (auto* obj_id_expr = std::get_if<declaration_node::object>(&decl->declaration->type)) {
if (auto* unqual = std::get_if<id_expression_node::unqualified>(&(*obj_id_expr)->id)){
is_pointer = !(*unqual)->pointer_declarators.empty();
}
}
}

// TODO: Generalize this -- for now we detect only multi-level cases of the form "p: ***int = ...;"
// We don't recognize pointer types that are deduced or from Cpp1
if (is_it_pointer_declaration(decl) || !unqual->pointer_declarators.empty() || is_pointer) {
if (n.ops.empty()) {
last_postfix_expr_was_pointer = true;
}
Expand Down Expand Up @@ -2155,10 +2195,6 @@ class cppfront
}
else {
emit( id_expr );
if (n.declaration->pointer_declarator) {
printer.print_cpp2(" ", n.declaration->pointer_declarator->position());
emit(*n.declaration->pointer_declarator);
}
}

// Then any suffix
Expand Down Expand Up @@ -2538,9 +2574,6 @@ class cppfront
}
printer.preempt_position(n.position());
emit( *type );
if (n.pointer_declarator) {
printer.print_cpp2("*", n.position());
}
// one pointer is enough for now, pointer-to-function fun can be later
if (!n.initializer) {
printer.print_cpp2( ">", n.position() );
Expand Down
43 changes: 29 additions & 14 deletions source/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ struct unqualified_id_node
{
token const* const_qualifier = {}; // optional
token const* identifier = {}; // required
std::vector<token const*> pointer_declarators = {}; // optional

enum active { empty=0, expression, id_expression };

Expand Down Expand Up @@ -846,7 +847,9 @@ struct declaration_node
source_position pos;
std::unique_ptr<unqualified_id_node> identifier;

token const* pointer_declarator = nullptr;
token const* dereference = nullptr;
int dereference_cnt = 0;
token const* address_of = nullptr;

enum active { function, object };
std::variant<
Expand Down Expand Up @@ -1771,13 +1774,19 @@ class parser
{
// Handle the identifier
if (curr().type() != lexeme::Identifier &&
curr().type() != lexeme::Keyword) // 'const', and fundamental types that are keywords
curr().type() != lexeme::Keyword && // 'const', and fundamental types that are keywords
curr().type() != lexeme::Multiply ) // pointer declarators
{
return {};
}

auto n = std::make_unique<unqualified_id_node>();

while (curr().type() == lexeme::Multiply) {
n->pointer_declarators.push_back(&curr());
next();
}

if (curr().type() == lexeme::Keyword && curr() == "const") {
n->const_qualifier = &curr();
next();
Expand Down Expand Up @@ -2784,6 +2793,7 @@ class parser
auto unnamed_declaration(source_position pos, bool semicolon_required = true, bool captures_allowed = false) -> std::unique_ptr<declaration_node>
{
auto deduced_type = false;
auto pointer_declaration = false;

// The next token must be :
if (curr().type() != lexeme::Colon) {
Expand All @@ -2809,19 +2819,11 @@ class parser
n->type = std::move(t);
assert (n->type.index() == declaration_node::function);
}

// Or a pointer to a type, declaring a pointer object
else if (curr().type() == lexeme::Multiply) {
n->pointer_declarator = &curr();
next();
if (auto t = id_expression()) {
n->type = std::move(t);
assert (n->type.index() == declaration_node::object);
}
}

// Or just a type, declaring a non-pointer object
else if (auto t = id_expression()) {
if(auto* unqual = std::get_if<id_expression_node::unqualified>(&t->id)) {
pointer_declaration = !(*unqual)->pointer_declarators.empty();
}
n->type = std::move(t);
assert (n->type.index() == declaration_node::object);
}
Expand Down Expand Up @@ -2866,7 +2868,7 @@ class parser
n->equal_sign = curr().position();
next();

if (n->pointer_declarator) {
if (pointer_declaration) {
if (curr() == "nullptr" ||
isdigit(std::string_view(curr())[0]) ||
(curr() == "(" && peek(1) && *peek(1) == ")")
Expand All @@ -2878,6 +2880,19 @@ class parser
}
}

if (deduced_type) {
if (peek(1)->type() == lexeme::Ampersand) {
n->address_of = &curr();
}
else if (peek(1)->type() == lexeme::Multiply) {
n->dereference = &curr();
n->dereference_cnt = 1;
while(peek(n->dereference_cnt+1)->type() == lexeme::Multiply) {
n->dereference_cnt += 1;
}
}
}

if (!(n->initializer = statement(semicolon_required, n->equal_sign))) {
error("ill-formed initializer");
next();
Expand Down