Skip to content

Commit

Permalink
chore(compiler): type references as expressions and phase tracking (#…
Browse files Browse the repository at this point in the history
…3177)

No functional changes, preparatory pull request as part of the expression lifting escapade.

Type references are expressions that reference type names. It's valuable to model them as expressions because they are needed at runtime. Once they are modeled as expressions, we can treat all expressions equally when it comes to lifting (which is the motivation for this refactor).

We've changed the following places to use type references instead of a `UserDefinedType`. In all of these cases the types have a runtime presence. The rest of the places are just type annotations that are erased at runtime:

- The class being instantiated in `new` expressions
- Base classes
- The type component in a `Reference::TypeMember` (static access).

Furthermore, to support expression lifting, the type checker now records the phase of each expression during type checking and makes it available through `get_expr_phase`.

Additionally, changed the left-hand-side of an assignment statement to be an `Expr` and not a `Reference`. Also in order to be able to lift it.

Turn the variable component of `StmtKind::Assignment` from a simple `Symbol` to an `Expr` in order for it to also hold type and phase information.

The JSII importer accidentally imported structs as `preflight` while they should be phase-independent.

Misc: strip excess newlines when adding a line to `CodeMaker`.

## Checklist

- [x] Title matches [Winglang's style guide](https://docs.winglang.io/contributing/pull_requests#how-are-pull-request-titles-formatted)
- [x] Description explains motivation and solution
- [x] Tests added (always)
- [x] Docs updated (only required for features)
- [x] 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 [Monada Contribution License](https://docs.winglang.io/terms-and-policies/contribution-license.html)*.
  • Loading branch information
eladb authored Jun 30, 2023
1 parent f808f84 commit 9840f37
Show file tree
Hide file tree
Showing 23 changed files with 910 additions and 449 deletions.
4 changes: 2 additions & 2 deletions examples/tests/invalid/class.w
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ class C7 extends x {
struct S1 {}

class C8 extends S1 {
//^^ Preflight class C8's parent is not a class
//^^ Expected "S1" to be a class
}

class C11 extends C11 {
//^^^ Class cannot extend itself
//^^^ Unknown symbol "C11"
}

class Student {
Expand Down
4 changes: 2 additions & 2 deletions examples/tests/invalid/enums.w
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ enum SomeEnum {
}

let four = SomeEnum.FOUR;
// ERR ^^^^ enum value does not exist
// ERR ^^^^ Enum "SomeEnum" does not contain value "FOUR"

let two = SomeEnum.TWO.TWO;
// ERR ^^^
// ERR ^^^ Property not found
10 changes: 10 additions & 0 deletions examples/tests/invalid/inflight_reassign.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
let var xvar = "hello";
let ylet = 123;

inflight () => {
xvar = "hi";
//^^^^ Variable cannot be reassigned from inflight

ylet = 456;
//^^^^ Variable is not reassignable
};
7 changes: 7 additions & 0 deletions examples/tests/valid/custom_obj_id.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Foo { }

let foo1 = new Foo();
let bar2 = new Foo() as "bar2";

assert(foo1.node.id == "Foo");
assert(bar2.node.id == "bar2");
50 changes: 39 additions & 11 deletions libs/wingc/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ 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 @@ -319,11 +326,30 @@ pub struct Class {
pub methods: Vec<(Symbol, FunctionDefinition)>,
pub initializer: FunctionDefinition,
pub inflight_initializer: FunctionDefinition,
pub parent: Option<UserDefinedType>,
pub parent: Option<Expr>, // the expression must be 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;
};

let ExprKind::Reference(ref r) = expr.kind else {
return None;
};

let Reference::TypeReference(t) = r else {
return None;
};

Some(t.clone())
}
}

#[derive(Debug)]
pub struct Interface {
pub name: Symbol,
Expand Down Expand Up @@ -372,7 +398,7 @@ pub enum StmtKind {
Return(Option<Expr>),
Expression(Expr),
Assignment {
variable: Reference,
variable: Expr,
value: Expr,
},
Scope(Scope),
Expand Down Expand Up @@ -419,7 +445,7 @@ pub struct StructField {
#[derive(Debug)]
pub enum ExprKind {
New {
class: TypeAnnotation,
class: Box<Expr>, // expression must be a reference to a user defined type
obj_id: Option<Box<Expr>>,
obj_scope: Option<Box<Expr>>,
arg_list: ArgList,
Expand Down Expand Up @@ -592,8 +618,10 @@ 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 { type_: UserDefinedType, property: Symbol },
TypeMember { typeobject: Box<Expr>, property: Symbol },
}

impl Display for Reference {
Expand All @@ -611,13 +639,13 @@ impl Display for Reference {
};
write!(f, "{}.{}", obj_str, property.name)
}
Reference::TypeMember { type_, property } => {
write!(
f,
"{}.{}",
TypeAnnotationKind::UserDefined(type_.clone()),
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)
}
}
}
Expand Down
51 changes: 29 additions & 22 deletions libs/wingc/src/closure_transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,14 @@ impl Fold for ClosureTransformer {
span: WingSpan::default(),
};

let class_udt = UserDefinedType {
root: new_class_name.clone(),
fields: vec![],
span: WingSpan::default(),
};

let class_type_annotation = TypeAnnotation {
kind: TypeAnnotationKind::UserDefined(UserDefinedType {
root: new_class_name.clone(),
fields: vec![],
span: WingSpan::default(),
}),
kind: TypeAnnotationKind::UserDefined(class_udt.clone()),
span: WingSpan::default(),
};

Expand Down Expand Up @@ -177,21 +179,24 @@ impl Fold for ClosureTransformer {
let class_init_body = vec![Stmt {
idx: 0,
kind: StmtKind::Assignment {
variable: Reference::InstanceMember {
object: Box::new(Expr::new(
ExprKind::Reference(Reference::InstanceMember {
object: Box::new(Expr::new(
ExprKind::Reference(Reference::Identifier(Symbol::new("this", WingSpan::default()))),
WingSpan::default(),
)),
property: Symbol::new("display", WingSpan::default()),
optional_accessor: false,
}),
WingSpan::default(),
)),
property: Symbol::new("hidden", WingSpan::default()),
optional_accessor: false,
},
variable: Expr::new(
ExprKind::Reference(Reference::InstanceMember {
object: Box::new(Expr::new(
ExprKind::Reference(Reference::InstanceMember {
object: Box::new(Expr::new(
ExprKind::Reference(Reference::Identifier(Symbol::new("this", WingSpan::default()))),
WingSpan::default(),
)),
property: Symbol::new("display", WingSpan::default()),
optional_accessor: false,
}),
WingSpan::default(),
)),
property: Symbol::new("hidden", WingSpan::default()),
optional_accessor: false,
}),
WingSpan::default(),
),
value: Expr::new(ExprKind::Literal(Literal::Boolean(true)), WingSpan::default()),
},
span: WingSpan::default(),
Expand Down Expand Up @@ -272,7 +277,7 @@ impl Fold for ClosureTransformer {
// ```
let new_class_instance = Expr::new(
ExprKind::New {
class: class_type_annotation,
class: Box::new(class_udt.to_expression()),
arg_list: ArgList {
named_args: IndexMap::new(),
pos_args: vec![],
Expand Down Expand Up @@ -333,7 +338,9 @@ impl<'a> Fold for RenameThisTransformer<'a> {
Reference::Identifier(ident)
}
}
Reference::InstanceMember { .. } | Reference::TypeMember { .. } => fold::fold_reference(self, node),
Reference::InstanceMember { .. } | Reference::TypeMember { .. } | Reference::TypeReference(_) => {
fold::fold_reference(self, node)
}
}
}
}
4 changes: 2 additions & 2 deletions libs/wingc/src/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
jsify::codemaker::CodeMaker,
type_check::{
jsii_importer::is_construct_base, Class, FunctionSignature, Interface, Namespace, Struct, SymbolKind, Type,
TypeRef, VariableInfo,
TypeRef, VariableInfo, VariableKind,
},
};

Expand Down Expand Up @@ -94,7 +94,7 @@ impl Documented for VariableInfo {
fn render_docs(&self) -> String {
let mut modifiers = vec![];

if self.is_member && self.is_static {
if let VariableKind::StaticMember = self.kind {
modifiers.push("static");
}

Expand Down
11 changes: 6 additions & 5 deletions libs/wingc/src/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ where
StmtKind::Return(value) => StmtKind::Return(value.map(|value| f.fold_expr(value))),
StmtKind::Expression(expr) => StmtKind::Expression(f.fold_expr(expr)),
StmtKind::Assignment { variable, value } => StmtKind::Assignment {
variable: f.fold_reference(variable),
variable: f.fold_expr(variable),
value: f.fold_expr(value),
},
StmtKind::Scope(scope) => StmtKind::Scope(f.fold_scope(scope)),
Expand Down Expand Up @@ -193,7 +193,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_user_defined_type(parent)),
parent: node.parent.map(|parent| f.fold_expr(parent)),
implements: node
.implements
.into_iter()
Expand Down Expand Up @@ -257,7 +257,7 @@ where
obj_scope,
arg_list,
} => ExprKind::New {
class: f.fold_type_annotation(class),
class: Box::new(f.fold_expr(*class)),
obj_id,
obj_scope: obj_scope.map(|scope| Box::new(f.fold_expr(*scope))),
arg_list: f.fold_args(arg_list),
Expand Down Expand Up @@ -364,8 +364,9 @@ where
property: f.fold_symbol(property),
optional_accessor,
},
Reference::TypeMember { type_, property } => Reference::TypeMember {
type_: f.fold_user_defined_type(type_),
Reference::TypeReference(udt) => Reference::TypeReference(f.fold_user_defined_type(udt)),
Reference::TypeMember { typeobject, property } => Reference::TypeMember {
typeobject: Box::new(f.fold_expr(*typeobject)),
property: f.fold_symbol(property),
},
}
Expand Down
Loading

0 comments on commit 9840f37

Please sign in to comment.