4

I'm having trouble adding elements to an object that keeps a collection of generic-typed values. I tried a Minimal Working Example that causes the error:

class OneElementQueue {

    type eltType;

    var elements : [0..0] eltType;

    //initializer
    proc init(type eltType) {
        this.eltType = eltType;
    }

    proc add(element : eltType) {
        this.elements[0] = element;
    }

    proc remove() : eltType {
        return this.elements[0];
    }   
} //end of OneElementQueue

class Monkey {

    var name: string;
    var age: int;

    proc init(name : string, age : int) {
        this.name = name;
        this.age = age;
    }

} //end of class Monkey


var q = new owned OneElementQueue(Monkey);
var m = new owned Monkey("Kyle", 6);
q.add(m);

When I try to compile all of this, I get an error:

$ chpl BadQueue.chpl
BadQueue.chpl:12: In function 'add':
BadQueue.chpl:13: error: Scoped variable would outlive the value it is set to
BadQueue.chpl:12: note: consider scope of element
$

What is the correct way to go about adding something to a generic data structure like this? How am I going about this the wrong way?

Kyle
  • 554
  • 3
  • 10

1 Answers1

3

There are two possible approaches you can take here, depending on what behavior you want:

"I want to have my collection take ownership of the Monkey objects"

In this case, you'll want to instantiate your OneElementQueue collection to store owned Monkey objects rather than simply [borrowed] Monkey objects, which is the default for class types. You can do this with the one line change (Try it Online):

var q = new owned OneElementQueue(owned Monkey);

In this approach, passing an owned Monkey to your add() method will pass the ownership to the argument and eventually to the collection, making the original object reference invalid (nil).

"I want to have my collection borrow the existing Monkey objects without taking ownership of them"

In this case, you'll need to tell the add() method that the argument passed into it will outlive the argument itself (and then be sure not to lie about it). In Chapel version 1.19, this can be done via lifetime annotations:

proc add(element : eltType) lifetime element > this {

where the annotation lifetime element > this asserts that the actual argument passed through element will outlive the this collection itself, so the compiler should not fear that the borrow will cease to exist once the formal argument has.

Lifetime annotations were not available in Chapel 1.18, so if you're using that version you need to use a slightly bigger hammer and apply pragma "unsafe" to the method. Note that pragmas are not an officially supported feature and may change in the future, so for this case, served as a stopgap until lifetime annotations had been implemented (Try it Online):

pragma "unsafe"
proc add(element : eltType) {
Brad
  • 3,839
  • 7
  • 25
  • Follow up: if I want to create objects in code in one place, then put them into the data structure, then take them out elsewhere, I need to start them off as "unmanaged" and make sure I delete them after using them on the other end. Is that right? (And in this case, I'd have to use the unsafe pragma until version 19.) – Kyle Mar 01 '19 at 02:13
  • 1
    If by "take them out" you mean "permanently", then no, you could still use `owned`. Such an object would originally be owned by, e.g., the variable `m` in your program, then that ownership is transferred to the data structure, then when the object is removed, the ownership would be transferred to the variable that catches the object on the other side. Then when the final variable holding the object goes out of scope, the object is deleted. – Brad Mar 01 '19 at 04:43
  • 1
    ou could also take the approach you describe—using `unmanaged` and taking care to delete the variable yourself. But in this instance, the compiler tries to do nothing to reason about the lifetime of the variable and so the pragma would not be required. `unmanaged` is effectively a way to tell the compiler "You don't need to worry about this, I will." Whereas `owned` results in lifetime checking within the compiler and `shared` results in execution-time reference counting. – Brad Mar 01 '19 at 04:44