Skip to content

Commit

Permalink
selfhost: Allow field references in methods.
Browse files Browse the repository at this point in the history
Allow creating local references to struct fields in methods.

It turned out the way we were checking scope lifetimes was a bit
backwards. We use this method every to guard against the inverse (the
child scope outliving the parent) but the method wasn't implemented that
way. To be honest I still don't understand a 100% why some tests were
passing in the old implementation.

We I also had to invert the exit condition to account for the fact that
comptime scope parent chains are disjunct from runtime scope parent
chains, but at least we now know that the json parser sample is testing
for that case 😉.

Fixing this also surfaced that the `stores_arguments` attribute test
wasn't actually testing the feature but was passing due to the bug. I
had to adjust it to actually test the failure case for it to continue to
pass.

Progress on #1379.
  • Loading branch information
fahrradflucht committed Feb 19, 2023
1 parent f4bd913 commit d644589
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 19 deletions.
2 changes: 1 addition & 1 deletion samples/attributes/stores_arguments.jakt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ extern struct Vector<T> {

fn main() {
let x = 10i32
mut v = Vector<i32>()
{
let y = 10i32
mut v = Vector<i32>()
v.append(&x)
v.append(&y)
}
Expand Down
39 changes: 21 additions & 18 deletions selfhost/typechecker.jakt
Original file line number Diff line number Diff line change
Expand Up @@ -5111,7 +5111,7 @@ struct Typechecker {
spans: []
)

fn scope_lifetime_subsumes(this, anon larger: ScopeId?, anon smaller: ScopeId?, checked: &mut {String}) throws -> bool {
fn scope_lifetime_subsumes(this, anon larger: ScopeId?, anon smaller: ScopeId?) throws -> bool {
if not larger.has_value() {
return false
}
Expand All @@ -5123,36 +5123,39 @@ struct Typechecker {
let larger_id = larger!
let smaller_id = smaller!

if checked.contains(larger_id.to_string()) {
// If the scopes are equal, the smaller one is not a subscope.
if larger_id.equals(smaller_id) {
return false
}
checked.add(larger_id.to_string())

// Just go through the parent chain of the larger scope, if 'smaller' is a child of one (or equal to), assume it's a subscope.
mut scope_id = Some(larger_id)
mut first = true
while scope_id.has_value() {
if not first and scope_id!.equals(smaller_id) {
// Go through the parent chain of the smaller scope, if 'larger' is a
// equal to or a child of the parent, assume the smaller one is a
// subscope.
mut scope_id = smaller_id
loop {
let scope = .program.get_scope(scope_id)

guard scope.parent.has_value() else {
break
}

if scope.parent!.equals(larger_id) {
return true
}
first = false
let scope = .program.get_scope(scope_id!)
for child_scope_id in scope.children {
if .scope_lifetime_subsumes(child_scope_id, smaller_id, checked) {

let parent_scope = .program.get_scope(scope.parent!)
for child_scope_id in parent_scope.children {
if child_scope_id.equals(larger_id) {
return true
}
}
scope_id = scope.parent

scope_id = scope.parent!
}

return false
}

fn scope_lifetime_subsumes(this, anon larger: ScopeId?, anon smaller: ScopeId?) throws -> bool {
mut checked: {String} = {}
return .scope_lifetime_subsumes(larger, smaller, &mut checked)
}

fn scope_lifetime_union(this, anon first: ScopeId?, anon second: ScopeId?) throws -> ScopeId? {
// If one is immediate, the union is also immediate:
if not first.has_value() or not second.has_value() {
Expand Down
18 changes: 18 additions & 0 deletions tests/typechecker/field_pointer_lifetime.jakt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// Expect:
/// - output: "42\n"

struct Struct {
i: i64,

fn method(this) -> i64 {
let i_ptr = &.i

return *i_ptr
}
}

fn main() {
let s = Struct(i: 42)

println("{}", s.method())
}

0 comments on commit d644589

Please sign in to comment.