Skip to content

Commit

Permalink
fix(compiler): can't call methods in inflight class's init (#4390)
Browse files Browse the repository at this point in the history
see #4290
Compiler now treats `init`'s of inflight classes as an async function. This makes it possible to call other async functions (specifically the class's methods) in the init function like you would expect.

The PR also includes some minor naming and comment cleanups.

## Checklist

- [x] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted)
- [x] Description explains motivation and solution
- [x] Tests added (always)
- [ ] Docs updated (only required for features)
- [ ] Added `pr/e2e-full` label if this feature requires end-to-end testing

*By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*.
  • Loading branch information
yoav-steinberg authored Oct 7, 2023
1 parent 2ae3972 commit 31c8698
Show file tree
Hide file tree
Showing 37 changed files with 506 additions and 136 deletions.
11 changes: 10 additions & 1 deletion examples/tests/invalid/access_modifiers.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,21 @@ class Foo impl SomeInterface {
this.protected_method();
this.private_method();

// Check access from inner class
class InnerFoo {
method(f: Foo) {
// Check access from inner class
log(f.protected_field);
log(f.private_field);
log(f.public_field);

// Check access from 2nd level inner class (inner class nesting)
class InnerInnerFoo {
method(f: Foo) {
log(f.protected_field);
log(f.private_field);
log(f.public_field);
}
}
}
}
}
Expand Down
56 changes: 56 additions & 0 deletions examples/tests/valid/inflight_init.test.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
inflight class Foo {
pub field1: num;
pub field2: num;

get_six(): num {
return 6;
}

init(f2: num) {
this.field1 = this.get_six();
this.field2 = f2;
}
}

inflight class FooChild extends Foo {
pub field3: num;

init() {
super(5);
this.field3 = 4;
}
}

test "inflight class init" {
let f = new Foo(5);
// Make sure the init was called
assert(f.field1 == 6 && f.field2 == 5);
}

test "inflight calls parent's init" {
let f = new FooChild();
// Make sure the init was called and the parent's init was called
assert(f.field1 == 6 && f.field2 == 5 && f.field3 == 4);
}

test "inflight calls parent's init when non exists" {
// Note these are all inflight classes since we're in an inflight test context
class FooNoInit {
protected leet(): num {
return 1337;
}
}

class FooChild extends FooNoInit {
pub field: num;

init() {
super();
this.field = this.leet();
}
}

let f = new FooChild();
// Make sure the init was called and the parent's init was called
assert(f.field == 1337);
}
6 changes: 3 additions & 3 deletions libs/wingc/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ pub struct StructField {

#[derive(Debug)]
pub enum ExprKind {
New(NewExpr),
New(New),
Literal(Literal),
Range {
start: Box<Expr>,
Expand Down Expand Up @@ -646,8 +646,8 @@ impl Expr {
}

#[derive(Debug)]
pub struct NewExpr {
pub class: UserDefinedType, // expression must be a reference to a user defined type
pub struct New {
pub class: UserDefinedType,
pub obj_id: Option<Box<Expr>>,
pub obj_scope: Option<Box<Expr>>,
pub arg_list: ArgList,
Expand Down
6 changes: 3 additions & 3 deletions libs/wingc/src/closure_transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use indexmap::IndexMap;
use crate::{
ast::{
AccessModifier, ArgList, AssignmentKind, CalleeKind, Class, ClassField, Expr, ExprKind, FunctionBody,
FunctionDefinition, FunctionParameter, FunctionSignature, Literal, NewExpr, Phase, Reference, Scope, Stmt,
StmtKind, Symbol, TypeAnnotation, TypeAnnotationKind, UserDefinedType,
FunctionDefinition, FunctionParameter, FunctionSignature, Literal, New, Phase, Reference, Scope, Stmt, StmtKind,
Symbol, TypeAnnotation, TypeAnnotationKind, UserDefinedType,
},
diagnostic::WingSpan,
fold::{self, Fold},
Expand Down Expand Up @@ -304,7 +304,7 @@ impl Fold for ClosureTransformer {
// new <new_class_name>();
// ```
let new_class_instance = Expr::new(
ExprKind::New(NewExpr {
ExprKind::New(New {
class: class_udt,
arg_list: ArgList {
named_args: IndexMap::new(),
Expand Down
8 changes: 4 additions & 4 deletions libs/wingc/src/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
ast::{
ArgList, BringSource, CalleeKind, CatchBlock, Class, ClassField, ElifBlock, ElifLetBlock, Expr, ExprKind,
FunctionBody, FunctionDefinition, FunctionParameter, FunctionSignature, IfLet, Interface, InterpolatedString,
InterpolatedStringPart, Literal, NewExpr, Reference, Scope, Stmt, StmtKind, StructField, Symbol, TypeAnnotation,
InterpolatedStringPart, Literal, New, Reference, Scope, Stmt, StmtKind, StructField, Symbol, TypeAnnotation,
TypeAnnotationKind, UserDefinedType,
},
dbg_panic,
Expand Down Expand Up @@ -33,7 +33,7 @@ pub trait Fold {
fn fold_expr(&mut self, node: Expr) -> Expr {
fold_expr(self, node)
}
fn fold_new_expr(&mut self, node: NewExpr) -> NewExpr {
fn fold_new_expr(&mut self, node: New) -> New {
fold_new_expr(self, node)
}
fn fold_literal(&mut self, node: Literal) -> Literal {
Expand Down Expand Up @@ -342,11 +342,11 @@ where
}
}

pub fn fold_new_expr<F>(f: &mut F, node: NewExpr) -> NewExpr
pub fn fold_new_expr<F>(f: &mut F, node: New) -> New
where
F: Fold + ?Sized,
{
NewExpr {
New {
class: f.fold_user_defined_type(node.class),
obj_id: node.obj_id,
arg_list: f.fold_args(node.arg_list),
Expand Down
30 changes: 12 additions & 18 deletions libs/wingc/src/jsify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::{borrow::Borrow, cell::RefCell, cmp::Ordering, collections::BTreeMap, v
use crate::{
ast::{
ArgList, AssignmentKind, BinaryOperator, BringSource, CalleeKind, Class as AstClass, ElifLetBlock, Expr, ExprKind,
FunctionBody, FunctionDefinition, IfLet, InterpolatedStringPart, Literal, NewExpr, Phase, Reference, Scope, Stmt,
FunctionBody, FunctionDefinition, IfLet, InterpolatedStringPart, Literal, New, Phase, Reference, Scope, Stmt,
StmtKind, Symbol, UnaryOperator, UserDefinedType,
},
comp_ctx::{CompilationContext, CompilationPhase},
Expand Down Expand Up @@ -417,8 +417,8 @@ impl<'a> JSifier<'a> {
_ => "",
};
match &expression.kind {
ExprKind::New(new_expr) => {
let NewExpr { class, obj_id, arg_list, obj_scope } = new_expr;
ExprKind::New(new) => {
let New { class, obj_id, arg_list, obj_scope } = new;

let expression_type = self.types.get_expr_type(&expression);
let is_preflight_class = expression_type.is_preflight_class();
Expand Down Expand Up @@ -465,7 +465,13 @@ impl<'a> JSifier<'a> {
format!("this.node.root.new(\"{}\",{},{})", fqn, ctor, args)
}
} else {
format!("new {}({})", ctor, args)
// If we're inflight and this new expression evaluates to a type with an inflight init (that's not empty)
// make sure it's called before we return the object.
if ctx.visit_ctx.current_phase() == Phase::Inflight && expression_type.as_class().expect("a class").get_method(&Symbol::global(CLASS_INFLIGHT_INIT_NAME)).is_some() {
format!("(await (async () => {{const o = new {ctor}(); await o.{CLASS_INFLIGHT_INIT_NAME}?.({args}); return o; }})())")
} else {
format!("new {}({})", ctor, args)
}
}
}
ExprKind::Literal(lit) => match lit {
Expand Down Expand Up @@ -810,7 +816,7 @@ impl<'a> JSifier<'a> {
let args = self.jsify_arg_list(&arg_list, None, None, ctx);
match ctx.visit_ctx.current_phase() {
Phase::Preflight => CodeMaker::one_line(format!("super(scope,id,{});", args)),
_ => CodeMaker::one_line(format!("super({});", args)),
_ => CodeMaker::one_line(format!("await super.{CLASS_INFLIGHT_INIT_NAME}?.({});", args)),
}
}
StmtKind::Let {
Expand Down Expand Up @@ -1065,19 +1071,7 @@ impl<'a> JSifier<'a> {
}

let (name, arrow) = match &func_def.name {
Some(name) => {
let mut result = name.name.clone();

// if this is an inflight class, we need to rename the constructor to "constructor" because
// it's "just a class" basically.
if let Some(class) = class {
if result == CLASS_INFLIGHT_INIT_NAME && class.phase == Phase::Inflight {
result = JS_CONSTRUCTOR.to_string();
}
}

(result, " ".to_string())
}
Some(name) => (name.name.clone(), " ".to_string()),
None => ("".to_string(), " => ".to_string()),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ module.exports = function({ }) {
class Bar {
async func() {
x;
new Foo();
(await (async () => {const o = new Foo(); await o.$inflight_init?.(); return o; })());
}
constructor() {
async $inflight_init() {
x;
new Foo();
(await (async () => {const o = new Foo(); await o.$inflight_init?.(); return o; })());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ module.exports = function({ }) {
}
class Bar {
async myMethod() {
new Foo();
(await (async () => {const o = new Foo(); await o.$inflight_init?.(); return o; })());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module.exports = function({ }) {
async handle() {
class Foo {
}
new Foo();
(await (async () => {const o = new Foo(); await o.$inflight_init?.(); return o; })());
}
}
return $Closure1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module.exports = function({ $Foo }) {
return $obj;
}
async handle() {
new $Foo();
(await (async () => {const o = new $Foo(); await o.$inflight_init?.(); return o; })());
}
}
return $Closure1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module.exports = function({ $Base }) {
async handle() {
class Derived extends $Base {
}
new Derived();
(await (async () => {const o = new Derived(); await o.$inflight_init?.(); return o; })());
}
}
return $Closure1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module.exports = function({ }) {
async handle() {
const y = "hello";
class A {
constructor() {
async $inflight_init() {
{console.log(y)};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module.exports = function({ }) {
async getField() {
return this.field;
}
constructor() {
async $inflight_init() {
this.field = "hi";
}
}
Expand Down
2 changes: 1 addition & 1 deletion libs/wingc/src/jsify/snapshots/new_inflight_object.snap
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module.exports = function({ $Foo }) {
return $obj;
}
async handle() {
new $Foo();
(await (async () => {const o = new $Foo(); await o.$inflight_init?.(); return o; })());
}
}
return $Closure1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module.exports = function({ $Foo }) {
return $obj;
}
async handle() {
const f = new $Foo();
const f = (await (async () => {const o = new $Foo(); await o.$inflight_init?.(); return o; })());
}
}
return $Closure1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module.exports = function({ }) {
}
}
class B {
constructor() {
async $inflight_init() {
(await A.foo());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ module.exports = function({ $MyInflightClass }) {
return $obj;
}
async handle() {
const obj = new $MyInflightClass();
const obj = (await (async () => {const o = new $MyInflightClass(); await o.$inflight_init?.(); return o; })());
(await obj.putInBucket());
}
}
Expand Down
4 changes: 2 additions & 2 deletions libs/wingc/src/lsp/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use lsp_types::{
SignatureInformation,
};

use crate::ast::{CalleeKind, Expr, ExprKind, NewExpr, Symbol};
use crate::ast::{CalleeKind, Expr, ExprKind, New, Symbol};
use crate::docs::Documented;
use crate::lsp::sync::PROJECT_DATA;
use crate::lsp::sync::WING_TYPES;
Expand Down Expand Up @@ -49,7 +49,7 @@ pub fn on_signature_help(params: lsp_types::SignatureHelpParams) -> Option<Signa
&crate::ast::ArgList,
) = match &expr.kind {
ExprKind::New(new_expr) => {
let NewExpr { class, arg_list, .. } = new_expr;
let New { class, arg_list, .. } = new_expr;

let Some(t) = resolve_user_defined_type(class, &types.get_scope_env(&root_scope), 0).ok() else {
return None;
Expand Down
8 changes: 4 additions & 4 deletions libs/wingc/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use tree_sitter_traversal::{traverse, Order};
use crate::ast::{
AccessModifier, ArgList, AssignmentKind, BinaryOperator, BringSource, CalleeKind, CatchBlock, Class, ClassField,
ElifBlock, ElifLetBlock, Expr, ExprKind, FunctionBody, FunctionDefinition, FunctionParameter, FunctionSignature,
IfLet, Interface, InterpolatedString, InterpolatedStringPart, Literal, NewExpr, Phase, Reference, Scope, Spanned,
Stmt, StmtKind, StructField, Symbol, TypeAnnotation, TypeAnnotationKind, UnaryOperator, UserDefinedType,
IfLet, Interface, InterpolatedString, InterpolatedStringPart, Literal, New, Phase, Reference, Scope, Spanned, Stmt,
StmtKind, StructField, Symbol, TypeAnnotation, TypeAnnotationKind, UnaryOperator, UserDefinedType,
};
use crate::comp_ctx::{CompilationContext, CompilationPhase};
use crate::diagnostic::{report_diagnostic, Diagnostic, DiagnosticResult, WingSpan, ERR_EXPECTED_SEMICOLON};
Expand Down Expand Up @@ -1767,7 +1767,7 @@ impl<'s> Parser<'s> {
};

Ok(Expr::new(
ExprKind::New(NewExpr {
ExprKind::New(New {
class: class_udt,
obj_id,
arg_list: arg_list?,
Expand Down Expand Up @@ -2292,7 +2292,7 @@ impl<'s> Parser<'s> {

let type_span = self.node_span(&statement_node.child(0).unwrap());
Ok(StmtKind::Expression(Expr::new(
ExprKind::New(NewExpr {
ExprKind::New(New {
class: UserDefinedType {
root: Symbol::global(WINGSDK_STD_MODULE),
fields: vec![Symbol::global(WINGSDK_TEST_CLASS_NAME)],
Expand Down
Loading

0 comments on commit 31c8698

Please sign in to comment.