Skip to content

Commit

Permalink
fix(compiler): capture and lifting cleanups and fixes (#3899)
Browse files Browse the repository at this point in the history
* Types aren't expressions anymore. This reverts a change in #3213. Back then we turned all user defined types into expressions so we can lift/capture them the same way we handle expressions. The downside was that the type system didn't make sense anymore (we added a type variable kind and the symbol env wasn't normalized). A side effect is better error spans on unknown type errors.
* To handle capturing/lifting of Types I created a `Liftable` enum that represents AST elements that can be lifted. A lift token now points to a `Liftable` and not to an expression. Currently a `Liftable` can be either an expression or a `UserDefinedType`.
* Simplified and normalized the `Lifts` data structure stored per class. It now contains:
  * `lifts_qualifications` - Stores information for each class inflight method which preflight code it lifts and for that code which ops are performed on it. It's a map of map: "method" -> "preflight js code" -> "set of ops". This data structure is used for generating the lift bindings.
  * `captures` - Stores capture tokens and for each token the preflight js code used for capture and also whether this is a free capture or an object field. This is used to initialize the tokens in preflight.
  * `token_for_liftable` - Maps capturable AST elements (`Liftable`s) to a capture token. This is used for generating inflight code when we encounter a captured type or expression during jsification.
* Fixed a bug where a type referencing itself but calling an inflight static method wasn't properly lifted. No bindings were generated for it leading to potential permission issues. Added a snapshot test.
* Fixed bug parsing static method calls on json `Json.something()` generated a wrong AST expression where we wrapped a `TypeMember` reference inside a `InstanceMemeber` reference (instead of just being a `TypeMemeber` reference). So the end jsification result was `Json.something().something()`??
* Fixed visitor type annotation's default implementation to call `visit_user_defined_type` for user defined types.

## 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 Aug 27, 2023
1 parent 2c27b86 commit 5d5d46e
Show file tree
Hide file tree
Showing 31 changed files with 737 additions and 543 deletions.
62 changes: 23 additions & 39 deletions libs/wingc/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,26 @@ pub enum TypeAnnotationKind {

// In the future this may be an enum for type-alias, class, etc. For now its just a nested name.
// Also this root,fields thing isn't really useful, should just turn in to a Vec<Symbol>.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq)]
pub struct UserDefinedType {
pub root: Symbol,
pub fields: Vec<Symbol>,
pub span: WingSpan,
}

impl Hash for UserDefinedType {
fn hash<H: Hasher>(&self, state: &mut H) {
self.root.hash(state);
self.fields.hash(state);
}
}

impl PartialEq for UserDefinedType {
fn eq(&self, other: &Self) -> bool {
self.root == other.root && self.fields == other.fields
}
}

impl UserDefinedType {
pub fn for_class(class: &Class) -> Self {
Self {
Expand All @@ -173,13 +186,6 @@ impl UserDefinedType {
pub fn full_path_str(&self) -> String {
self.full_path().iter().join(".")
}

pub fn to_expression(&self) -> Expr {
Expr::new(
ExprKind::Reference(Reference::TypeReference(self.clone())),
self.span.clone(),
)
}
}

impl Display for UserDefinedType {
Expand Down Expand Up @@ -346,21 +352,12 @@ pub struct Class {
pub methods: Vec<(Symbol, FunctionDefinition)>,
pub initializer: FunctionDefinition,
pub inflight_initializer: FunctionDefinition,
pub parent: Option<Expr>, // base class (the expression is a reference to a user defined type)
pub parent: Option<UserDefinedType>, // base class (the expression is a reference to a user defined type)
pub implements: Vec<UserDefinedType>,
pub phase: Phase,
}

impl Class {
/// Returns the `UserDefinedType` of the parent class, if any.
pub fn parent_udt(&self) -> Option<&UserDefinedType> {
let Some(expr) = &self.parent else {
return None;
};

expr.as_type_reference()
}

/// Returns all methods, including the initializer and inflight initializer.
pub fn all_methods(&self, include_initializers: bool) -> Vec<&FunctionDefinition> {
let mut methods: Vec<&FunctionDefinition> = vec![];
Expand Down Expand Up @@ -606,19 +603,11 @@ impl Expr {
let id = EXPR_COUNTER.fetch_add(1, Ordering::SeqCst);
Self { id, kind, span }
}

/// Returns the user defined type if the expression is a reference to a type.
pub fn as_type_reference(&self) -> Option<&UserDefinedType> {
match &self.kind {
ExprKind::Reference(Reference::TypeReference(t)) => Some(t),
_ => None,
}
}
}

#[derive(Debug)]
pub struct NewExpr {
pub class: Box<Expr>, // expression must be a reference to a user defined type
pub class: UserDefinedType, // expression must be a reference to a user defined type
pub obj_id: Option<Box<Expr>>,
pub obj_scope: Option<Box<Expr>>,
pub arg_list: ArgList,
Expand Down Expand Up @@ -724,10 +713,11 @@ pub enum Reference {
property: Symbol,
optional_accessor: bool,
},
/// A reference to a type (e.g. `std.Json` or `MyResource` or `aws.s3.Bucket`)
TypeReference(UserDefinedType),
/// A reference to a member inside a type: `MyType.x` or `MyEnum.A`
TypeMember { typeobject: Box<Expr>, property: Symbol },
TypeMember {
type_name: UserDefinedType,
property: Symbol,
},
}

impl Spanned for Reference {
Expand All @@ -739,8 +729,7 @@ impl Spanned for Reference {
property,
optional_accessor: _,
} => object.span().merge(&property.span()),
Reference::TypeReference(type_) => type_.span(),
Reference::TypeMember { typeobject, property } => typeobject.span().merge(&property.span()),
Reference::TypeMember { type_name, property } => type_name.span().merge(&property.span()),
}
}
}
Expand All @@ -760,13 +749,8 @@ impl Display for Reference {
};
write!(f, "{}.{}", obj_str, property.name)
}
Reference::TypeReference(type_) => write!(f, "{}", type_),
Reference::TypeMember { typeobject, property } => {
let ExprKind::Reference(ref r) = typeobject.kind else {
return write!(f, "<?>.{}", property.name);
};

write!(f, "{}.{}", r, property.name)
Reference::TypeMember { type_name, property } => {
write!(f, "{}.{}", type_name, property.name)
}
}
}
Expand Down
19 changes: 7 additions & 12 deletions libs/wingc/src/closure_transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,11 @@ impl Fold for ClosureTransformer {
ExprKind::Call {
callee: CalleeKind::Expr(Box::new(Expr::new(
ExprKind::Reference(Reference::TypeMember {
typeobject: Box::new(Expr::new(
ExprKind::Reference(Reference::TypeReference(UserDefinedType {
root: Symbol::new("std", WingSpan::for_file(file_id)),
fields: vec![Symbol::new("Node", WingSpan::for_file(file_id))],
span: WingSpan::for_file(file_id),
})),
WingSpan::for_file(file_id),
)),
type_name: UserDefinedType {
root: Symbol::new("std", WingSpan::for_file(file_id)),
fields: vec![Symbol::new("Node", WingSpan::for_file(file_id))],
span: WingSpan::for_file(file_id),
},
property: Symbol::new("of", WingSpan::for_file(file_id)),
}),
WingSpan::for_file(file_id),
Expand Down Expand Up @@ -304,7 +301,7 @@ impl Fold for ClosureTransformer {
// ```
let new_class_instance = Expr::new(
ExprKind::New(NewExpr {
class: Box::new(class_udt.to_expression()),
class: class_udt,
arg_list: ArgList {
named_args: IndexMap::new(),
pos_args: vec![],
Expand Down Expand Up @@ -365,9 +362,7 @@ impl<'a> Fold for RenameThisTransformer<'a> {
Reference::Identifier(ident)
}
}
Reference::InstanceMember { .. } | Reference::TypeMember { .. } | Reference::TypeReference(_) => {
fold::fold_reference(self, node)
}
Reference::InstanceMember { .. } | Reference::TypeMember { .. } => fold::fold_reference(self, node),
}
}
}
9 changes: 4 additions & 5 deletions libs/wingc/src/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ where
.map(|(name, def)| (f.fold_symbol(name), f.fold_function_definition(def)))
.collect(),
initializer: f.fold_function_definition(node.initializer),
parent: node.parent.map(|parent| f.fold_expr(parent)),
parent: node.parent.map(|parent| f.fold_user_defined_type(parent)),
implements: node
.implements
.into_iter()
Expand Down Expand Up @@ -342,7 +342,7 @@ where
F: Fold + ?Sized,
{
NewExpr {
class: Box::new(f.fold_expr(*node.class)),
class: f.fold_user_defined_type(node.class),
obj_id: node.obj_id,
arg_list: f.fold_args(node.arg_list),
obj_scope: node.obj_scope,
Expand Down Expand Up @@ -386,9 +386,8 @@ where
property: f.fold_symbol(property),
optional_accessor,
},
Reference::TypeReference(udt) => Reference::TypeReference(f.fold_user_defined_type(udt)),
Reference::TypeMember { typeobject, property } => Reference::TypeMember {
typeobject: Box::new(f.fold_expr(*typeobject)),
Reference::TypeMember { type_name, property } => Reference::TypeMember {
type_name: f.fold_user_defined_type(type_name),
property: f.fold_symbol(property),
},
}
Expand Down
Loading

0 comments on commit 5d5d46e

Please sign in to comment.