Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/haven.ebnf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module ::= top_decl+
top_decl ::= import
| cimport
| foreign_block
| visibility_block
| fn_decl
| tydecl
| extend_decl
Expand Down Expand Up @@ -35,7 +36,7 @@ params ::= param (',' param)* (',' '*')?
| '*'
param ::= type IDENT

tydecl ::= 'type' IDENT ( '=' type_body )? ';'
tydecl ::= visibility? 'type' IDENT ( '=' type_body )? ';'
type_body ::= type
| struct_decl
| enum_decl
Expand All @@ -59,7 +60,9 @@ global_data ::= 'data' global_binding
global_state ::= 'state' global_binding
global_binding ::= type IDENT ( '=' expr )?

visibility_block ::= visibility '{' top_decl* '}'
visibility ::= 'pub'
| 'pub' '(' 'module' ')'
mutability ::= 'mut'

block ::= '{' stmt* expr? '}'
Expand Down
6 changes: 4 additions & 2 deletions docs/haven.lark
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module: top_decl+
top_decl: import
| cimport
| foreign_block
| visibility_block
| fn_decl
| tydecl
| global_decl
Expand Down Expand Up @@ -34,7 +35,7 @@ params: param ("," param)* ("," "*")?
| "*"
param: type IDENT

tydecl: "type" IDENT ("=" type_body)? ";"
tydecl: visibility? "type" IDENT ("=" type_body)? ";"
type_body: type
| struct_decl
| enum_decl
Expand All @@ -52,7 +53,8 @@ global_data: "data" global_binding
global_state: "state" global_binding
global_binding: type IDENT ("=" expr)?

visibility: "pub"
visibility_block: visibility "{" top_decl* "}"
visibility: "pub" | "pub" "(" "module" ")"
mutability: "mut"

block: "{" stmt* expr? "}"
Expand Down
27 changes: 24 additions & 3 deletions docs/language.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,27 @@ control the mutability of the stored value. In the example above, `:=` would be

## Declarations

### Visibility

Top-level declarations are file-visible by default. Use `pub(module)` for declarations shared by files in the same module, and `pub` for declarations visible outside the module.

```haven
fn file_helper() -> i32 { 1 }
pub(module) fn module_helper() -> i32 { 2 }
pub fn public_helper() -> i32 { 3 }
```

Visibility blocks are shorthand for applying the same visibility to each declaration in the block:

```haven
pub(module) {
fn first_helper() -> i32 { 1 }
fn second_helper() -> i32 { 2 }
}
```

An explicit declaration modifier overrides the surrounding block. Visibility blocks do not introduce a lexical scope.

### Import Declarations

#### Haven Imports
Expand Down Expand Up @@ -445,11 +466,11 @@ Variables at function scope must be initialized.
Functions can be forward-declared without a body.

```
[pub] [impure] fn <ident>(<arg-list>) -> <ret-ty>;
[pub] [impure] fn <ident>(<arg-list>) -> <ret-ty> { <body> }
[visibility] [impure] fn <ident>(<arg-list>) -> <ret-ty>;
[visibility] [impure] fn <ident>(<arg-list>) -> <ret-ty> { <body> }
```

Specifying `pub` on declarations that have no definitions will create an external reference to the function.
Specifying external `pub` on declarations that have no definitions will create an external reference to the function. A `pub(module)` declaration without a definition remains module-visible and is not exported at linker scope.

Specifying `impure` on declarations will mark the function as impure, which means it is allowed to read and write memory.

Expand Down
33 changes: 30 additions & 3 deletions docs/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ referenced by:
top_decl ::= import
| cimport
| foreign_block
| visibility_block
| fn_decl
| tydecl
| extend_decl
Expand All @@ -48,6 +49,17 @@ referenced by:

* top_decl

**visibility_block:**

```
visibility_block
::= visibility '{' top_decl* '}'
```

referenced by:

* top_decl

**extend_decl:**

```
Expand Down Expand Up @@ -156,7 +168,7 @@ referenced by:

```
fn_header
::= 'pub'? fn_purity 'fn' IDENT '(' params? ')' return_type?
::= visibility? fn_purity 'fn' IDENT '(' params? ')' return_type?
```

referenced by:
Expand Down Expand Up @@ -249,7 +261,7 @@ referenced by:
![tydecl](diagram/tydecl.svg)

```
tydecl ::= 'type' IDENT ( '=' type_body )? ';'
tydecl ::= visibility? 'type' IDENT ( '=' type_body )? ';'
```

referenced by:
Expand Down Expand Up @@ -379,13 +391,28 @@ referenced by:

```
global_decl
::= 'pub'? ( global_data | global_state ) ';'
::= visibility? ( global_data | global_state ) ';'
```

referenced by:

* top_decl

**visibility:**

```
visibility
::= 'pub'
| 'pub' '(' 'module' ')'
```

referenced by:

* fn_header
* tydecl
* global_decl
* visibility_block

**global_data:**

![global_data](diagram/global_data.svg)
Expand Down
5 changes: 4 additions & 1 deletion src/bin/lsp/code_lenses.ml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ let function_lenses (pipeline : Analysis.Pipeline.result) (fn : Core.function_de
(fun title -> title)
[
Some (if fn.value.impure then "impure" else "pure");
if fn.value.public then Some "public" else None;
(match fn.value.visibility with
| Haven_core.Visibility.File -> None
| Module -> Some "module"
| External -> Some "public");
Option.map
(fun (intrinsic : Core.intrinsic) ->
"intrinsic " ^ intrinsic.value.name.value)
Expand Down
9 changes: 7 additions & 2 deletions src/bin/lsp/document_symbols.ml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ let function_detail (fn : Cst.function_decl) =
(List.filter
(fun part -> not (String.equal part ""))
[
if fn.value.public then "pub" else "";
(match fn.value.visibility with
| Haven_core.Visibility.File -> ""
| Module -> "pub(module)"
| External -> "pub");
if fn.value.impure then "impure" else "";
"fn";
])
Expand Down Expand Up @@ -122,8 +125,10 @@ let type_symbol (decl : Cst.type_decl) =
~selection_range:(Lsp_helpers.loc_to_range decl.value.name.loc) ()

let symbols_for_program (parsed : Cst.parsed_program) =
let top_decl_symbols (decl : Cst.top_decl) =
let rec top_decl_symbols (decl : Cst.top_decl) =
match decl.value with
| Cst.VisibilityBlock block ->
List.concat_map top_decl_symbols block.value.decls
| Cst.FDecl fn ->
[ function_symbol fn ]
| Cst.VDecl var_decl ->
Expand Down
21 changes: 16 additions & 5 deletions src/bin/lsp/symbol_resolution.ml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,10 @@ let function_signature (fn : Core.function_decl) =
(List.filter
(fun part -> not (String.equal part ""))
[
if fn.value.public then "pub" else "";
(match fn.value.visibility with
| Haven_core.Visibility.File -> ""
| Module -> "pub(module)"
| External -> "pub");
if fn.value.impure then "impure" else "";
"fn";
])
Expand All @@ -136,7 +139,7 @@ let function_signature (fn : Core.function_decl) =
Printf.sprintf "%s %s(%s)%s" prefix fn.value.name.value
(String.concat ", " params) return_suffix

let function_signature_with_types ~name ~public ~impure ~vararg ~params
let function_signature_with_types ~name ~visibility ~impure ~vararg ~params
~return_type =
let params =
let params =
Expand All @@ -151,7 +154,12 @@ let function_signature_with_types ~name ~public ~impure ~vararg ~params
String.concat " "
(List.filter
(fun part -> not (String.equal part ""))
[ if public then "pub" else ""; if impure then "impure" else ""; "fn" ])
[ (match visibility with
| Haven_core.Visibility.File -> ""
| Module -> "pub(module)"
| External -> "pub");
if impure then "impure" else "";
"fn" ])
in
let return_suffix =
match return_type with
Expand Down Expand Up @@ -216,7 +224,10 @@ let binding_contents typing (binding : Core.let_stmt) =
let global_contents (decl : Core.var_decl) =
hover_block
(Printf.sprintf "global%s%s %s: %s"
(if decl.value.public then " pub" else "")
(match decl.value.visibility with
| Haven_core.Visibility.File -> ""
| Module -> " pub(module)"
| External -> " pub")
(if decl.value.is_mutable then " mut" else "")
decl.value.name.value
(format_core_type decl.value.ty))
Expand Down Expand Up @@ -429,7 +440,7 @@ and specialized_call_hover state (expr : Core.expression) (call : Core.call) =
| Some params ->
let specialized =
function_signature_with_types ~name:fn.value.name.value
~public:fn.value.public ~impure:fn.value.impure
~visibility:fn.value.visibility ~impure:fn.value.impure
~vararg:fn.value.vararg ~params
~return_type:
(Option.map
Expand Down
50 changes: 43 additions & 7 deletions src/lib/ast/convert.ml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,39 @@ let validate_surface_function_decl (fn : Surface.function_decl) =
fn.value.name.value (string_of_loc fn.loc))

let rec cst_program_to_surface (program : Cst.program) : Surface.program =
let decls = List.map cst_top_decl_to_surface program.value.decls in
let rec expand_decl ~default_visibility (decl : Cst.top_decl) =
match decl.value with
| Cst.VisibilityBlock block ->
List.concat_map
(expand_decl ~default_visibility:block.value.visibility)
block.value.decls
| Cst.FDecl fn ->
let fn =
if fn.value.visibility = Visibility.File then
{ fn with value = { fn.value with visibility = default_visibility } }
else fn
in
[ cst_top_decl_to_surface { decl with value = Cst.FDecl fn } ]
| Cst.TDecl ty ->
let ty =
if ty.value.visibility = Visibility.File then
{ ty with value = { ty.value with visibility = default_visibility } }
else ty
in
[ cst_top_decl_to_surface { decl with value = Cst.TDecl ty } ]
| Cst.VDecl var ->
let var =
if var.value.visibility = Visibility.File then
{ var with value = { var.value with visibility = default_visibility } }
else var
in
[ cst_top_decl_to_surface { decl with value = Cst.VDecl var } ]
| Cst.Import _ | Cst.CImport _ | Cst.Foreign _ | Cst.Extend _ ->
[ cst_top_decl_to_surface decl ]
in
let decls =
List.concat_map (expand_decl ~default_visibility:Visibility.File) program.value.decls
in
mk_surface program.loc { Surface.decls }

and cst_top_decl_to_surface (decl : Cst.top_decl) : Surface.top_decl =
Expand All @@ -112,6 +144,8 @@ and cst_top_decl_to_surface (decl : Cst.top_decl) : Surface.top_decl =
| Cst.FDecl fn -> Surface.FDecl (cst_function_decl_to_surface fn)
| Cst.TDecl ty -> Surface.TDecl (cst_type_decl_to_surface ty)
| Cst.VDecl v -> Surface.VDecl (cst_var_decl_to_surface v)
| Cst.VisibilityBlock _ ->
failwith "visibility blocks must be expanded before surface conversion"
| Cst.Import i -> Surface.Import { value = i.value; loc = i.loc }
| Cst.CImport i -> Surface.CImport { value = i.value; loc = i.loc }
| Cst.Foreign f -> Surface.Foreign (cst_foreign_to_surface f)
Expand All @@ -122,7 +156,7 @@ and cst_top_decl_to_surface (decl : Cst.top_decl) : Surface.top_decl =
and cst_function_decl_to_surface (fn : Cst.function_decl) : Surface.function_decl =
let value =
{
Surface.public = fn.value.public;
Surface.visibility = fn.value.visibility;
impure = fn.value.impure;
name = cst_identifier_to_surface fn.value.name;
definition = Option.map cst_block_to_surface fn.value.definition;
Expand Down Expand Up @@ -167,7 +201,7 @@ and cst_var_decl_to_surface (decl : Cst.var_decl) : Surface.var_decl =
let value =
{
Surface.name = cst_identifier_to_surface decl.value.name;
public = decl.value.public;
Surface.visibility = decl.value.visibility;
is_mutable = decl.value.is_mutable;
ty = cst_type_to_surface decl.value.ty;
init_expr = Option.map cst_expr_to_surface decl.value.init_expr;
Expand Down Expand Up @@ -216,6 +250,7 @@ and cst_type_decl_to_surface (decl : Cst.type_decl) : Surface.type_decl =
let value =
{
Surface.name = cst_identifier_to_surface decl.value.name;
Surface.visibility = decl.value.visibility;
data = cst_type_decl_data_to_surface decl.value.data;
construct = None;
destruct = None;
Expand Down Expand Up @@ -797,7 +832,7 @@ let synthesize_surface_lifecycle_fn
let self_ty = mk_surface_pointer_type loc target_ty in
mk_surface loc
{
Surface.public = false;
Surface.visibility = Visibility.File;
impure = true;
name = mk_surface_ident loc (lifecycle_function_name target kind);
definition = Some body;
Expand Down Expand Up @@ -938,7 +973,7 @@ and surface_top_decl_to_core st (decl : Surface.top_decl) : Core.top_decl =
and surface_function_decl_to_core st (fn : Surface.function_decl) : Core.function_decl =
let value =
{
Core.public = fn.value.public;
Core.visibility = fn.value.visibility;
impure = fn.value.impure;
name = surface_identifier_to_core fn.value.name;
definition =
Expand Down Expand Up @@ -978,7 +1013,7 @@ and surface_var_decl_to_core st (decl : Surface.var_decl) : Core.var_decl =
mk_core decl.loc
{
Core.name = surface_identifier_to_core decl.value.name;
public = decl.value.public;
Core.visibility = decl.value.visibility;
is_mutable = decl.value.is_mutable;
ty = surface_type_to_core decl.value.ty;
init_expr = Option.map (surface_expr_to_core st) decl.value.init_expr;
Expand All @@ -988,6 +1023,7 @@ and surface_type_decl_to_core st (decl : Surface.type_decl) : Core.type_decl =
mk_core decl.loc
{
Core.name = surface_identifier_to_core decl.value.name;
visibility = decl.value.visibility;
data = surface_type_decl_data_to_core st decl.value.data;
construct = Option.map (surface_function_decl_to_core st) decl.value.construct;
destruct = Option.map (surface_function_decl_to_core st) decl.value.destruct;
Expand Down Expand Up @@ -1046,7 +1082,7 @@ and surface_foreign_to_core st (foreign : Surface.foreign) : Core.foreign =
value =
{
fn.value with
public = true;
Surface.visibility = Visibility.External;
impure = true;
};
}
Expand Down
Loading
Loading