Skip to content

Commit

Permalink
feat(compiler)!: access modifiers for classes, structs, interfaces, e…
Browse files Browse the repository at this point in the history
…nums (winglang#4591)

Wing now supports declaring classes, structs, interfaces, and enums as public using the `pub` access modifier. The default access of all types is now private.

This PR also fixes a bug where you could not `bring` modules under the same alias in different files.

Closes winglang#4294
Closes winglang#4415
Closes winglang#2763

BREAKING CHANGE: All types are private by default. To make a class, struct, or other declaration accessible to other files, prefix it with the `pub` access modifier.

## 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)
- [x] 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
Chriscbr authored Oct 19, 2023
1 parent 677a56a commit 2b00ad4
Show file tree
Hide file tree
Showing 41 changed files with 620 additions and 221 deletions.
1 change: 0 additions & 1 deletion docs/docs/03-language-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1146,7 +1146,6 @@ The following features are not yet implemented, but we are planning to add them
### 1.14 Roadmap
* Module type visibility (exports/`pub` types) is not implemented yet - see https://github.com/winglang/wing/issues/130 to track.
* `internal` access modifier is not yet implemented - see https://github.com/winglang/wing/issues/4156 to track.
## 2. Statements
Expand Down
18 changes: 18 additions & 0 deletions examples/tests/invalid/access_private_apis.test.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
bring "./subdir2" as subdir2;

new subdir2.MyPrivateClass();
// error: Class "MyPrivateClass" is private

new subdir2.inner.MyOtherPrivateClass();
// error: Class "MyOtherPrivateClass" is private
// TODO: error: namespace "inner" is private
// (because it has no public API elements)

class A impl subdir2.MyPrivateInterface {}
// error: Interface "MyPrivateInterface" is private

let s = subdir2.MyPrivateStruct {};
// error: Struct "MyPrivateStruct" is private

let e = subdir2.MyPrivateEnum.A;
// error: Enum "MyPrivateEnum" is private
2 changes: 1 addition & 1 deletion examples/tests/invalid/file_with_variables.w
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ let x = 5;
let y = ["hello", "world"];
let z = new cloud.Bucket();

class Bar {
pub class Bar {
x: num;
}

11 changes: 11 additions & 0 deletions examples/tests/invalid/protected_access_modifiers.test.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
protected struct MyStruct {}
// ^ Structs must be public or private

protected class MyClass {}
// ^ Classes must be public or private

protected interface MyInterface {}
// ^ Interfaces must be public or private

protected enum MyEnum {}
// ^ Enums must be public or private
2 changes: 1 addition & 1 deletion examples/tests/invalid/subdir/inner/foo1.w
Original file line number Diff line number Diff line change
@@ -1 +1 @@
class Foo {}
pub class Foo {}
2 changes: 1 addition & 1 deletion examples/tests/invalid/subdir/inner/foo2.w
Original file line number Diff line number Diff line change
@@ -1 +1 @@
class Foo {}
pub class Foo {}
7 changes: 7 additions & 0 deletions examples/tests/invalid/subdir2/file.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class MyPrivateClass {}

struct MyPrivateStruct {}

enum MyPrivateEnum { A, B }

interface MyPrivateInterface {}
1 change: 1 addition & 0 deletions examples/tests/invalid/subdir2/inner/blah.w
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class MyOtherPrivateClass {}
2 changes: 1 addition & 1 deletion examples/tests/sdk_tests/state/my-service.w
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ bring sim;
bring cloud;
bring util;

class MyService {
pub class MyService {
pub state: sim.State;
startTimeKey: str;
pub startTime: str;
Expand Down
4 changes: 2 additions & 2 deletions examples/tests/valid/assertions.w
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// - api_cors_default.test.w
// - website_with_api.test.w

inflight class Assert {
pub inflight class Assert {
pub static equalStr(a: str, b: str): bool {
try {
assert(a == b);
Expand All @@ -29,4 +29,4 @@ inflight class Assert {
throw("expected: ${b} got: ${a}");
}
}
}
}
2 changes: 1 addition & 1 deletion examples/tests/valid/baz.w
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// used by:
// - bring_local_normalization.test.w

class Baz {
pub class Baz {
pub static baz(): str {
return "baz";
}
Expand Down
10 changes: 5 additions & 5 deletions examples/tests/valid/store.w
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ bring "./subdir/empty.w" as file3;
bring math;
bring cloud;

class Util {}
pub class Util {}

class Store {
pub class Store {
b: cloud.Bucket;
init() {
this.b = new cloud.Bucket();
Expand All @@ -21,17 +21,17 @@ class Store {
}
}

enum Color {
pub enum Color {
RED,
GREEN,
BLUE,
}

struct Point {
pub struct Point {
x: num;
y: num;
}

interface Shape {
pub interface Shape {
area(): num;
}
2 changes: 1 addition & 1 deletion examples/tests/valid/subdir/bar.w
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// used by bring_local_normalization.w

class Bar {
pub class Bar {
pub static bar(): str {
return "bar";
}
Expand Down
2 changes: 1 addition & 1 deletion examples/tests/valid/subdir/foo.w
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
bring "./bar.w" as bar;
bring "../baz.w" as baz;

class Foo {
pub class Foo {
pub static foo(): str {
return "foo";
}
Expand Down
6 changes: 3 additions & 3 deletions examples/tests/valid/subdir/structs.w
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
struct MyStruct {
pub struct MyStruct {
val: num;
}

struct MyOtherStruct {
pub struct MyOtherStruct {
data: MyStruct;
}
}
8 changes: 4 additions & 4 deletions examples/tests/valid/subdir/structs_2.w
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
struct MyStruct {
pub struct MyStruct {
val: str;
}

struct SomeStruct {
pub struct SomeStruct {
foo: str;
}

class UsesStructInImportedFile {
pub class UsesStructInImportedFile {
someStruct: SomeStruct;

init() {
this.someStruct = SomeStruct.fromJson({foo: "123"});
}
}
}
2 changes: 1 addition & 1 deletion examples/tests/valid/subdir/subfile.w
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
bring math;

class Q {
pub class Q {
extern "./util.js" static inflight greet(name: str): str;
}
3 changes: 2 additions & 1 deletion examples/tests/valid/subdir2/file1.w
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
bring "./inner" as blah; // an alias that is not "inner"
bring util;

class Foo {
pub class Foo {
pub foo(): str {
return "foo";
}
Expand Down
8 changes: 7 additions & 1 deletion examples/tests/valid/subdir2/file2.w
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
class Bar {
bring util;

pub class Bar {
pub bar(): str {
return "bar";
}
}

// this file's sibling, file1.w, also has a class "Foo"
// but this is okay since it's private
class Foo {}
2 changes: 1 addition & 1 deletion examples/tests/valid/subdir2/inner/widget.w
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class Widget {
pub class Widget {
pub compute(): num {
return 42;
}
Expand Down
2 changes: 1 addition & 1 deletion examples/wing-fixture/enums.w
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
enum FavoriteNumbers {
pub enum FavoriteNumbers {
SEVEN,
FORTY_TWO,
}
2 changes: 1 addition & 1 deletion examples/wing-fixture/store.w
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
bring cloud;
bring "./subdir/util.w" as myutil;

class Store {
pub class Store {
data: cloud.Bucket;
init() {
this.data = new cloud.Bucket();
Expand Down
2 changes: 1 addition & 1 deletion examples/wing-fixture/subdir/util.w
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class Util {
pub class Util {
pub static inflight double(msg: str): str {
return "${msg}${msg}";
}
Expand Down
27 changes: 5 additions & 22 deletions libs/tree-sitter-wing/grammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ module.exports = grammar({
$.variable_assignment_statement,
$.return_statement,
$.class_definition,
$.resource_definition,
$.interface_definition,
$.for_in_loop,
$.while_statement,
Expand All @@ -131,6 +130,7 @@ module.exports = grammar({

struct_definition: ($) =>
seq(
optional(field("access_modifier", $.access_modifier)),
"struct",
field("name", $.identifier),
optional(seq("extends", commaSep(field("extends", $.custom_type)))),
Expand All @@ -141,6 +141,7 @@ module.exports = grammar({

enum_definition: ($) =>
seq(
optional(field("access_modifier", $.access_modifier)),
"enum",
field("enum_name", $.identifier),
braced(commaSep(alias($.identifier, $.enum_field)))
Expand Down Expand Up @@ -185,7 +186,8 @@ module.exports = grammar({
// Classes
class_definition: ($) =>
seq(
$.inflight_specifier,
optional(field("access_modifier", $.access_modifier)),
optional(field("phase_modifier", $.inflight_specifier)),
"class",
field("name", $.identifier),
optional(seq("extends", field("parent", $.custom_type))),
Expand Down Expand Up @@ -217,28 +219,9 @@ module.exports = grammar({
$._semicolon
),

resource_definition: ($) =>
seq(
"class",
field("name", $.identifier),
optional(seq("extends", field("parent", $.custom_type))),
optional(seq("impl", field("implements", commaSep1($.custom_type)))),
field("implementation", $.resource_implementation)
),
resource_implementation: ($) =>
braced(
repeat(
choice(
$.initializer,
$.method_definition,
$.inflight_method_definition,
$.class_field
)
)
),

interface_definition: ($) =>
seq(
optional(field("access_modifier", $.access_modifier)),
"interface",
field("name", $.identifier),
optional(seq("extends", field("extends", commaSep1($.custom_type)))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ inflight class A {

(source
(class_definition
(inflight_specifier)
phase_modifier: (inflight_specifier)
name: (identifier)
implementation: (class_implementation
(initializer
Expand Down Expand Up @@ -59,7 +59,7 @@ inflight class A extends B {}

(source
(class_definition
(inflight_specifier)
phase_modifier: (inflight_specifier)
name: (identifier)
parent: (custom_type
object: (type_identifier))
Expand Down Expand Up @@ -90,9 +90,9 @@ class A {
--------------------------------------------------------------------------------

(source
(resource_definition
(class_definition
name: (identifier)
implementation: (resource_implementation
implementation: (class_implementation
(initializer
parameter_list: (parameter_list)
block: (block))
Expand Down Expand Up @@ -177,15 +177,15 @@ class A extends B impl C, D {}
--------------------------------------------------------------------------------

(source
(resource_definition
(class_definition
name: (identifier)
parent: (custom_type
object: (type_identifier))
implements: (custom_type
object: (type_identifier))
implements: (custom_type
object: (type_identifier))
implementation: (resource_implementation)))
implementation: (class_implementation)))

================================================================================
Interface definition
Expand Down
8 changes: 6 additions & 2 deletions libs/wingc/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ pub struct FunctionDefinition {
/// Whether this function is static or not. In case of a closure, this is always true.
pub is_static: bool,
/// Function's access modifier. In case of a closure, this is always public.
pub access_modifier: AccessModifier,
pub access: AccessModifier,
pub span: WingSpan,
}

Expand Down Expand Up @@ -361,6 +361,7 @@ pub struct Class {
pub parent: Option<UserDefinedType>, // base class (the expression is a reference to a user defined type)
pub implements: Vec<UserDefinedType>,
pub phase: Phase,
pub access: AccessModifier,
}

impl Class {
Expand Down Expand Up @@ -422,6 +423,7 @@ pub struct Interface {
pub name: Symbol,
pub methods: Vec<(Symbol, FunctionSignature)>,
pub extends: Vec<UserDefinedType>,
pub access: AccessModifier,
}

#[derive(Debug)]
Expand Down Expand Up @@ -501,10 +503,12 @@ pub enum StmtKind {
name: Symbol,
extends: Vec<UserDefinedType>,
fields: Vec<StructField>,
access: AccessModifier,
},
Enum {
name: Symbol,
values: IndexSet<Symbol>,
access: AccessModifier,
},
TryCatch {
try_statements: Scope,
Expand All @@ -527,7 +531,7 @@ pub struct ClassField {
pub reassignable: bool,
pub phase: Phase,
pub is_static: bool,
pub access_modifier: AccessModifier,
pub access: AccessModifier,
}

#[derive(Debug, Clone, Copy, PartialEq)]
Expand Down
Loading

0 comments on commit 2b00ad4

Please sign in to comment.