Unable to capture preflight object from within an inflight class
I tried this:
bring cloud;
let b = new cloud.Bucket();
let myConst = "bang bang";
inflight class Foo {
uploadToBucket(k: str, value: str) {
b.put(k, value);
}
}
test "inflight class captures preflight resource" {
let f = new Foo();
f.uploadToBucket("hello.txt", "world");
}
This happened:
│ Error: Missing environment variable: BUCKET_HANDLE_ed7b9545
I expected this:
To work
Is there a workaround?
No response
Component
Language Design
Wing Version
No response
Wing Console Version
No response
Node.js Version
No response
Platform(s)
No response
Anything else?
No response
Community Notes
- Please vote by adding a 👍 reaction to the issue to help us prioritize.
- If you are interested to work on this issue, please leave a comment.
Just ran into this
Hi,
This issue hasn't seen activity in 60 days. Therefore, we are marking this issue as stale for now. It will be closed after 7 days. Feel free to re-open this issue when there's an update or relevant information to be added. Thanks!
note to self: the cause of the issue is that the expression f above is an inflight expression (because it references an inflight object). Inflight expressions aren't lifted so the compiler doesn't store binding information (add_lift) for the call expression f.uploadToBucket. This in turn means that we don't setup access to b or "lift" it when setting up the test.
Another related example:
inflight class Foo {
inflight foo: cloud.Secret;
new(foo: cloud.Secret) {
this.foo = foo;
}
inflight do() {
this.foo.value();
}
}
❯ wing compile main.w
error: Expression of type "Secret" references an unknown preflight object, can't qualify its capabilities. Use `lift()` to explicitly qualify the preflight object to disable this error.
--> main.w:10:5
|
10 | this.foo.value();
| ^^^^^^^^
Just wanted to add some more context to @hasanaburayyan's comment. Even though the current compiler error message is offers a workaround for some Wing applications, it seems to be a problem for Wing libraries where we don't know which preflight object is going to be lifted ahead of time.
In the example below, Slack has an inflight method postMessage() which therein creates an inflight class Foo. But the secret object passed to Foo's constructor is an object provided by the consumer of the library, so it's not possible to add a lift() statement to silence the compiler error.
inflight class Foo {
inflight s: cloud.Secret;
new(s: cloud.Secret) {
this.s = s;
}
inflight getValue(): str {
return this.foo.value(); // error: Expression of type "Secret" references an unknown preflight object
}
}
class Slack {
secret: cloud.Secret;
new(secret: cloud.Secret) {
this.secret = secret;
}
inflight postMessage() {
let foo = new Foo(this.secret);
let apiKey = foo.getValue();
}
}
// library usage
let s = new cloud.Secret();
new Slack(s);
It's worth noting that the limitation here parallels the existing limitations with passing objects through to inflight functions:
let getValue = inflight (s: cloud.Secret): str => {
return s.value(); // error: Expression of type "Secret" references an unknown preflight object
};
class Slack {
secret: cloud.Secret;
new(secret: cloud.Secret) {
this.secret = secret;
}
inflight postMessage() {
let apiKey = getValue(this.secret);
}
}
// library usage
let s = new cloud.Secret();
new Slack(s);
A note about the difficulty of resolving this issue: I've tries three different approaches all with their pros and cons and all seem to end up with the same hurdle that needs to be addressed. First the approaches:
- Split class
liftMaps to a type lift map vs an instnace lift map. The type lift map includes lifts of non-fields which don't change between instances. This is the case in all static methods, in all methods of inflight classes and also when lifting globals in instance methods. Then when we need to lift/qualify a usage of an inflight class's method we do this through the type lift map. - Create a dummy (even singleton) preflight instance of inflight classes (defined preflight). These are then used to lift/qualify usage of such inflight classes. Resulting in usable lift maps when using inflight classes.
- Hoist the lift maps of inflight classes (defined preflight) onto the the calling class's lift map. This way we avoid needing a preflight instance of the inflight class (or using a static type-lift-map). Instead the inflight classes lift map is never used but we add a phase during lifting where we hoist the lift information from inflight classes up into whoever's using them (upding the user's lift map).
All of these approaches run into a similar issue: At the point of usage of an inflight class, the preflight code of the inflight class might not be executable. The type of that preflight class might be shadowed or out of scope, the lifted expressions might use preflight variables that are out of scope, etc.
The only way around this seems to be using a global variables to store some references to the preflight class's inflight code, be it the type, the singleton, or the lifted expressions. Achieving a global scope isn't trivial either because this needs to be duplicated for each copy of the construct tree in case we have multiple copies in our preflight node process (like for our testing framework).
Perhaps for now, let's just add a compiler error in this case and not support it. At least it won't feel like a bug.
Congrats! :rocket: This was released in Wing 0.75.15.