From 2f404b7f08faf9f46afc0cfde8ae8967598ea0aa Mon Sep 17 00:00:00 2001 From: Ara Adkins Date: Tue, 16 Jun 2020 13:52:11 +0100 Subject: [PATCH] Fix a bug with compiler metadata (#838) 1. The metadata objects weren't being duplicated when duplicating the IR. This meant that the later passes would write metadata multiples times into one store (reference), causing wrong behaviour at codegen time. --- docs/syntax/README.md | 2 + docs/syntax/assignment.md | 2 +- docs/syntax/comments.md | 2 +- docs/syntax/function-arguments.md | 2 +- docs/syntax/functions.md | 2 +- docs/syntax/imports.md | 118 +++++ docs/syntax/literals.md | 2 +- docs/syntax/macros.md | 2 +- docs/syntax/projections.md | 2 +- docs/syntax/top-level.md | 2 +- docs/syntax/types.md | 48 +- docs/types/access-modifiers.md | 48 +- docs/types/modules.md | 38 +- .../org/enso/compiler/codegen/AstToIr.scala | 15 +- .../org/enso/compiler/codegen/AstView.scala | 17 + .../enso/compiler/codegen/IrToTruffle.scala | 1 + .../scala/org/enso/compiler/core/IR.scala | 495 ++++++++---------- .../compiler/core/ir/DiagnosticStorage.scala | 134 +++++ .../compiler/core/ir/MetadataStorage.scala | 19 +- .../scala/org/enso/compiler/pass/IRPass.scala | 10 + .../compiler/pass/analyse/AliasAnalysis.scala | 6 + .../analyse/CachePreferenceAnalysis.scala | 2 + .../pass/analyse/DataflowAnalysis.scala | 2 + .../pass/analyse/GatherDiagnostics.scala | 3 + .../enso/compiler/pass/analyse/TailCall.scala | 4 + .../compiler/pass/desugar/ComplexType.scala | 12 +- .../pass/desugar/NestedPatternMatch.scala | 26 +- .../pass/desugar/SectionsToBinOp.scala | 36 +- .../pass/optimise/ApplicationSaturation.scala | 10 + .../pass/resolve/DocumentationComments.scala | 2 + .../pass/resolve/IgnoredBindings.scala | 4 + .../pass/resolve/TypeSignatures.scala | 3 + .../DiagnosticStorageTest.scala} | 24 +- .../test/core/ir/MetadataStorageTest.scala | 4 + .../test/instrument/RuntimeServerTest.scala | 2 +- .../main/scala/org/enso/syntax/text/AST.scala | 48 +- .../enso/syntax/text/ast/meta/Builtin.scala | 89 ++-- .../org/enso/syntax/text/ParserTest.scala | 2 - 38 files changed, 795 insertions(+), 445 deletions(-) create mode 100644 docs/syntax/imports.md create mode 100644 engine/runtime/src/main/scala/org/enso/compiler/core/ir/DiagnosticStorage.scala rename engine/runtime/src/test/scala/org/enso/compiler/test/core/{IRTest.scala => ir/DiagnosticStorageTest.scala} (86%) diff --git a/docs/syntax/README.md b/docs/syntax/README.md index bd7ae50d780..ac6e56df79a 100644 --- a/docs/syntax/README.md +++ b/docs/syntax/README.md @@ -29,6 +29,8 @@ The various components of Enso's syntax are described below: - [**Encoding:**](./encoding.md) The source encoding of Enso files. - [**Naming:**](./naming.md) The naming of Enso language constructs. - [**Layout Rules:**](./layout.md) The layout rules for Enso code. +- [**Imports and Exports:**](./imports.md) The syntax and rules for importing + modules and exporting module functions. - [**Literals:**](./literals.md) The syntax for Enso literals. - [**Assignment:**](./assignment.md) The syntax for various forms of assignment expression in Enso. diff --git a/docs/syntax/assignment.md b/docs/syntax/assignment.md index 8696a5e6872..b988f18586c 100644 --- a/docs/syntax/assignment.md +++ b/docs/syntax/assignment.md @@ -3,7 +3,7 @@ layout: developer-doc title: Assignment Expressions category: syntax tags: [syntax, assignment] -order: 5 +order: 6 --- # Assignment Expressions diff --git a/docs/syntax/comments.md b/docs/syntax/comments.md index a03c51a53d8..3ce0b9e827e 100644 --- a/docs/syntax/comments.md +++ b/docs/syntax/comments.md @@ -3,7 +3,7 @@ layout: developer-doc title: Comments category: syntax tags: [syntax, comments] -order: 12 +order: 13 --- # Comments diff --git a/docs/syntax/function-arguments.md b/docs/syntax/function-arguments.md index c9bfc0fdfc3..f92ac64eca8 100644 --- a/docs/syntax/function-arguments.md +++ b/docs/syntax/function-arguments.md @@ -3,7 +3,7 @@ layout: developer-doc title: Function Arguments category: syntax tags: [syntax, functions] -order: 10 +order: 11 --- # Function Arguments diff --git a/docs/syntax/functions.md b/docs/syntax/functions.md index 23af5b5bf41..c7821192404 100644 --- a/docs/syntax/functions.md +++ b/docs/syntax/functions.md @@ -3,7 +3,7 @@ layout: developer-doc title: Defining Functions category: syntax tags: [syntax, functions] -order: 9 +order: 10 --- # Defining Functions diff --git a/docs/syntax/imports.md b/docs/syntax/imports.md new file mode 100644 index 00000000000..b8788402a69 --- /dev/null +++ b/docs/syntax/imports.md @@ -0,0 +1,118 @@ +--- +layout: developer-doc +title: Imports and Exports +category: syntax +tags: [syntax, imports, modules] +order: 4 +--- + +# Imports and Exports +In order to properly modularise and work with Enso code, the language provides +a robust mechanism for importing code from modules, and also re-exporting that +code from modules. + + + +- [Import Syntax](#import-syntax) + - [Visibility of Imported Bindings](#visibility-of-imported-bindings) +- [Export Syntax](#export-syntax) + - [Visibility of Export Bindings](#visibility-of-export-bindings) + + + +## Import Syntax +Importing a module is a way to bring its contents into scope in the current +module. Imports in Enso appear as follows: + +- They start with the `import` keyword. +- The `import` keyword is followed by a module path (e.g. `Base.Vector.Unsafe`). + +From there, Enso imports are broken up into four main categories: + +1. **Unqualified Imports:** These import all symbols from the module into the + current scope, and consist only of the `import` keyword and a module path. + Imported items are imported accessible without qualification. +2. **Qualified Imports:** These import all symbols from the module into the + current scope with symbols qualified under a name _different_ from the + module name. It consists of the `import` keyword followed by a module path + followed by the `as` keyword, followed by a referent name. +3. **Restricted Imports:** These import only the specific symbols from the + module into the current scope. They consist of the `import` keyword, followed + by a module path, followed by the `only` keyword, followed by a + space-separated list of symbols. +4. **Hiding Imports:** These are the inverse of restricted imports, and import + _all_ symbols other than the named ones into the current scope. They consist + of the `import` keyword, followed by a module path, followed by the keyword + `only`, followed by a space-separated list of symbols. + +The qualified import syntax can be combined with the restricted and hiding +import syntaxes. + +By way of example, the following code uses a variety of import types: + +```ruby +import A # unqualified +import B as T # qualified +import C only symbol_1 symbol_2 # restricted +import D hiding symbol_1 symbol_2 # hiding +import E as U only symbol_3 # qualified + restricted +import F as V hiding symbol_4 # qualified + hiding +``` +Imports in Enso _may_ introduce ambiguous symbols, but this is not an error +until one of the ambiguous symbols is _used_ in Enso code. + +### Visibility of Imported Bindings +When importing a module `X` into the current module `Y`, the bindings from `X` +made available by the import (see above) become available in `Y`. However, Enso +does not re-export imported bindings from a module by default, so the imported +bindings from `X` are not visible in a module _importing_ `Y`. + +## Export Syntax +In order to allow for easy composition and aggregation of code, Enso provides +its users with a mechanism to _export_ imported elements from modules. They +appear in Enso as follows: + +- They start with the `export` keyword. +- The `export` keyword is followed by a module name (e.g. `My_Module`) that is + available in the current scope. +- The _current_ module is implicitly exported unqualified. + +From there, Enso exports are broken up into four main categories: + +1. **Unqualified Exports:** These export all symbols from the named module as + if they were defined in the exporting module. They consist of the `export` + keyword, followed by a visible module name. +2. **Qualified Exports:** These export all symbols from the module as if they + were defined in a module nested within the exporting module. It consists of + the `export` keyword, followed by a module name, followed by the `as` + keyword, followed by another module name. +3. **Restricted Exports:** These export only the specified symbols from the + exporting module, as if they were defined in the exporting module. It + consists of the `export` keyword, followed by a module name, followed by the + `only` keyword, and then a space-separated list of symbols. +4. **Hiding Exports:** These export all symbols from the module _except_ those + explicitly specified. It consists of the `export` keyword, followed by a + module name, followed by the `hiding` keyword, and then a space-separated + list of symbols. + +The qualified export syntax can be combined with the restricted and hiding +export syntaxes. + +By way of example, the following code uses a variety of export types: + +```ruby +export A +export B as X +export C only symbol_1 +export D hiding symbol_1 +export E as Y only symbol_1 +export F as Y hiding symbol_1 +``` + +In essence, an export allows the user to `paste` the contents of the module +being exported into the module declaring the export. This means that exports +that create name clashes need to be resolved at the _export_ site. + +### Visibility of Export Bindings +Bindings exported from a module `X` are available in an identical fashion to +bindings that are _defined_ in the module `X`. diff --git a/docs/syntax/literals.md b/docs/syntax/literals.md index ca9b999f140..4abba2dc32f 100644 --- a/docs/syntax/literals.md +++ b/docs/syntax/literals.md @@ -3,7 +3,7 @@ layout: developer-doc title: Literals category: syntax tags: [syntax, literals] -order: 4 +order: 5 --- # Literals diff --git a/docs/syntax/macros.md b/docs/syntax/macros.md index 866d42f7916..2ad873384b5 100644 --- a/docs/syntax/macros.md +++ b/docs/syntax/macros.md @@ -3,7 +3,7 @@ layout: developer-doc title: The Enso Macro Syntax category: syntax tags: [syntax, macro] -order: 7 +order: 8 --- # The Enso Macro Syntax diff --git a/docs/syntax/projections.md b/docs/syntax/projections.md index d7ad926e8c0..3fa99ab826b 100644 --- a/docs/syntax/projections.md +++ b/docs/syntax/projections.md @@ -3,7 +3,7 @@ layout: developer-doc title: Projections and Field Access category: syntax tags: [syntax, projections, field-access, pattern-matching] -order: 11 +order: 12 --- # Projections and Field Access diff --git a/docs/syntax/top-level.md b/docs/syntax/top-level.md index 57c5614c27d..bec633d598b 100644 --- a/docs/syntax/top-level.md +++ b/docs/syntax/top-level.md @@ -3,7 +3,7 @@ layout: developer-doc title: Enso Top-Level Syntax category: syntax tags: [syntax, top-level, file] -order: 8 +order: 9 --- # Enso Top-Level Syntax diff --git a/docs/syntax/types.md b/docs/syntax/types.md index fc2819b3115..2f071e8626e 100644 --- a/docs/syntax/types.md +++ b/docs/syntax/types.md @@ -3,7 +3,7 @@ layout: developer-doc title: Types and Type Signatures category: syntax tags: [syntax, types] -order: 6 +order: 7 --- # Types and Type Signatures @@ -319,18 +319,48 @@ sometimes the case that it is necessary to indicate that certain fields should not be touched (as this might break invariants and such like). To this end, we propose an explicit mechanism for access modification that works as follows: -- We provide explicit access modifiers that, at the definition site, start an - indented block. These are `private` and `unsafe`. -- All members in the block have the access modifier attributed to them. -- By default, accessing any member under an access modifier will be an error. -- To use members under an access modifier, you use the syntax `use `, where - `` is a modifier. This syntax 'takes' an expression, including blocks, - within which the user may access members qualified by the modifier ``. +- We have a set of access modifiers, namely `private` and `unsafe`. +- We can place these modifiers before a top-level definition. + + ```ruby + type MyAtomType + type MyAtom a + + is_foo : Boolean + is_foo = ... + + private private_method a b = ... + + unsafe unsafe_method a b = ... + ``` + +- By default, accessing any member under an access modifier is an error when + performed from another module. +- To use members protected by an access modifier, you must _import_ that access + modifier from the file in which you want to access those elements. + + ```ruby + import private Base.Vector + import unsafe Base.Atom + ``` + +- These modified imports are available in _all_ scopes, so it is possible to + limit the scope in which you have access to the modified definitions. + + ```ruby + function_using_modifiers v x = + import private Base.Vector + import unsafe Base.Atom + + v.mutate_at_index 0 (_ -> x) + x = MyAtom.mutate_field name="sum" (with = x -> x + 20) + x + 20 + ``` While `private` works as you might expect, coming from other languages, the `unsafe` annotation has additional restrictions: -- It must be explicitly imported from `Std.Unsafe`. +- It must be explicitly imported from `Base.Unsafe`. - When you use `unsafe`, you must write a documentation comment on its usage that contains a section `Safety` that describes why this usage of unsafe is valid. diff --git a/docs/types/access-modifiers.md b/docs/types/access-modifiers.md index fc8425ed75c..9cdbf89a13e 100644 --- a/docs/types/access-modifiers.md +++ b/docs/types/access-modifiers.md @@ -23,13 +23,43 @@ provides an explicit mechanism for access modification. ## Access Modification Access modifiers in Enso work as follows: -- We provide explicit access modifiers that, at the definition site, start an - indented block. -- All members in the block have the access modifier attributed to them. -- By default, accessing any member under an access modifier will be an error. -- To use members under an access modifier, you use the syntax `use `, where - `` is a modifier. This syntax 'takes' an expression, including blocks, - within which the user may access members qualified by the modifier ``. +- We have a set of access modifiers, namely `private` and `unsafe`. +- We can place these modifiers before a top-level definition. + + ```ruby + type MyAtomType + type MyAtom a + + is_foo : Boolean + is_foo = ... + + private private_method a b = ... + + unsafe unsafe_method a b = ... + ``` + +- By default, accessing any member under an access modifier is an error when + performed from another module. +- To use members protected by an access modifier, you must _import_ that access + modifier from the file in which you want to access those elements. + + ```ruby + import private Base.Vector + import unsafe Base.Atom + ``` + +- These modified imports are available in _all_ scopes, so it is possible to + limit the scope in which you have access to the modified definitions. + + ```ruby + function_using_modifiers v x = + import private Base.Vector + import unsafe Base.Atom + + v.mutate_at_index 0 (_ -> x) + x = MyAtom.mutate_field name="sum" (with = x -> x + 20) + x + 20 + ``` > The actionables for this section are: > @@ -40,13 +70,13 @@ The `private` modifier acts to hide implementation details from clients of the API. It is: - Available by default in the `Base` library. -- Able to be overridden using the above-described mechanism. +- Able to be avoided using the above-described mechanism. ## Unsafe While `private` works as you might expect, coming from other languages, the `unsafe` annotation has additional restrictions: -- It must be explicitly imported from `Std.Unsafe`. +- It must be explicitly imported from `Base.Unsafe`. - When you use `unsafe`, you must write a documentation comment on its usage that contains a section `Safety` that describes why this usage of unsafe is valid. diff --git a/docs/types/modules.md b/docs/types/modules.md index e3b3afc662a..b8670aabc84 100644 --- a/docs/types/modules.md +++ b/docs/types/modules.md @@ -37,16 +37,16 @@ most languages, Enso provides an _import_ mechanism for this. Enso has four different kinds of imports that may be combined freely, all of which take a module path as their first argument. -1. **Unqualified Imports:** These import all symbols from the module into the - current scope (`import M`). -2. **Qualified Imports:** These import all symbols from the module into the - current scope with symbols qualified under a name _different_ from the - module name (`import M as T`). -3. **Restricted Imports:** These import only the specific symbols from the - module into the current scope (`import M only sym1 sym2`). -4. **Hiding Imports:** These are the inverse of restricted imports, and import - _all_ symbols other than the named ones into the current scope - (`import M hiding sym1 sym2`), +1. **Unqualified Imports:** These import all symbols from the module into the + current scope (`import M`). +2. **Qualified Imports:** These import all symbols from the module into the + current scope with symbols qualified under a name _different_ from the + module name (`import M as T`). +3. **Restricted Imports:** These import only the specific symbols from the + module into the current scope (`import M only sym1 sym2`). +4. **Hiding Imports:** These are the inverse of restricted imports, and import + _all_ symbols other than the named ones into the current scope + (`import M hiding sym1 sym2`), Imports may introduce ambiguous symbols, but this is not an error until one of the ambiguous symbols is used in user code. @@ -58,15 +58,15 @@ mechanism. Similarly to imports, this has four kinds, all of which take a module path as their first argument, and all of which _may_ introduce the module it exports into scope (if it is not already imported). -1. **Unqualified Exports:** These export all symbols from the module as if they - were defined in the exporting module (`export X`). -2. **Qualified Exports:** These export all symbols from the module as if they - were defined in another module accessible in the exporting module - (`export X as Y`). -3. **Restricted Exports:** These export only the specified symbols from the - module as if they were defined in the exporting module (`export X only sym`) -4. **Hiding Exports:** These export all symbols from the module except those - explicitly specified (`export X hiding sym1 sym2`). +1. **Unqualified Exports:** These export all symbols from the module as if they + were defined in the exporting module (`export X`). +2. **Qualified Exports:** These export all symbols from the module as if they + were defined in another module accessible in the exporting module + (`export X as Y`). +3. **Restricted Exports:** These export only the specified symbols from the + module as if they were defined in the exporting module (`export X only sym`) +4. **Hiding Exports:** These export all symbols from the module except those + explicitly specified (`export X hiding sym1 sym2`). Exports effectively act to 'paste' the contents of the exported module into the module declaring the export. This means that exports that create name clashes diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstToIr.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstToIr.scala index bed048943fa..33dc38e9dfc 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstToIr.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstToIr.scala @@ -818,11 +818,16 @@ object AstToIr { * @param imp the import to translate * @return the [[IR]] representation of `imp` */ - def translateImport(imp: AST.Import): Module.Scope.Import.Module = { - Module.Scope.Import.Module( - imp.path.map(t => t.name).reduceLeft((l, r) => l + "." + r), - getIdentifiedLocation(imp) - ) + def translateImport(imp: AST.Import): Module.Scope.Import = { + imp.path match { + case AstView.ModulePath(segments) => + IR.Module.Scope.Import.Module( + segments.map(_.name).mkString("."), + getIdentifiedLocation(imp.path) + ) + case _ => + IR.Error.Syntax(imp, IR.Error.Syntax.InvalidImport) + } } /** Translates an arbitrary invalid expression from the [[AST]] representation diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstView.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstView.scala index 214b98e048c..24b890004ac 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstView.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstView.scala @@ -405,6 +405,23 @@ object AstView { } } + object ModulePath { + + /** Matches on a module path of the form `A.B.C...`, as seen in an import. + * + * @param ast the structure to try and match on + * @return the list of segments in the module path + */ + def unapply(ast: AST): Option[List[AST.Ident]] = ast match { + case AST.Ident.Cons.any(name) => Some(List(name)) + case OperatorDot(left, AST.Ident.Cons.any(name)) => left match { + case ModulePath(elems) => Some(elems :+ name) + case _ => None + } + case _ => None + } + } + object SuspendDefaultsOperator { /** Matches on a usage of the `...` 'suspend defaults' operator. diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index 0276def66ad..e1741dffd21 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -147,6 +147,7 @@ class IrToTruffle( ) case i: Import.Module => this.moduleScope.addImport(context.getCompiler.processImport(i.name)) + case _: Error => } // Register the atoms and their constructors in scope diff --git a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala index 530ee726fd0..b1241b96d40 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala @@ -2,12 +2,8 @@ package org.enso.compiler.core import java.util.UUID -import org.enso.compiler.core.IR.{ - DiagnosticStorage, - Expression, - IdentifiedLocation -} -import org.enso.compiler.core.ir.MetadataStorage +import org.enso.compiler.core.IR.{Expression, IdentifiedLocation} +import org.enso.compiler.core.ir.{DiagnosticStorage, MetadataStorage} import org.enso.compiler.core.ir.MetadataStorage.MetadataPair import org.enso.compiler.exception.CompilerError import org.enso.compiler.pass.IRPass @@ -228,10 +224,11 @@ object IR { keepDiagnostics: Boolean = true ): Empty = copy( - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def setLocation(location: Option[IdentifiedLocation]): Empty = @@ -315,10 +312,11 @@ object IR { bindings = bindings.map( _.duplicate(keepLocations, keepMetadata, keepDiagnostics) ), - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def setLocation(location: Option[IdentifiedLocation]): Module = @@ -440,9 +438,10 @@ object IR { ): Module = copy( location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -540,9 +539,10 @@ object IR { ): Polyglot = copy( location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -637,9 +637,10 @@ object IR { _.duplicate(keepLocations, keepMetadata, keepDiagnostics) ), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -742,9 +743,10 @@ object IR { _.duplicate(keepLocations, keepMetadata, keepDiagnostics) ), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -858,9 +860,11 @@ object IR { body = body.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy + else DiagnosticStorage(), id = randomId ) @@ -970,9 +974,11 @@ object IR { body = body.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy + else DiagnosticStorage(), id = randomId ) @@ -1119,9 +1125,10 @@ object IR { returnValue = returnValue.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -1215,9 +1222,10 @@ object IR { expression = expression.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -1304,9 +1312,10 @@ object IR { ): Number = copy( location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -1373,9 +1382,10 @@ object IR { ): Text = copy( location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -1495,10 +1505,11 @@ object IR { typePointer.duplicate(keepLocations, keepMetadata, keepDiagnostics), methodName = methodName.duplicate(keepLocations, keepMetadata, keepDiagnostics), - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def mapExpressions( @@ -1633,9 +1644,10 @@ object IR { ): Blank = copy( location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -1703,9 +1715,10 @@ object IR { ): Literal = copy( location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -1770,9 +1783,10 @@ object IR { ): This = copy( location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -1837,9 +1851,10 @@ object IR { ): Here = copy( location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -1934,9 +1949,10 @@ object IR { signature = signature.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -2020,9 +2036,10 @@ object IR { context = context.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -2099,12 +2116,13 @@ object IR { keepMetadata: Boolean = true, keepDiagnostics: Boolean = true ): Error = copy( - typed = typed.duplicate(keepLocations, keepMetadata, keepDiagnostics), - error = error.duplicate(keepLocations, keepMetadata, keepDiagnostics), - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + typed = typed.duplicate(keepLocations, keepMetadata, keepDiagnostics), + error = error.duplicate(keepLocations, keepMetadata, keepDiagnostics), + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def setLocation(location: Option[IdentifiedLocation]): Error = @@ -2201,9 +2219,10 @@ object IR { memberType.duplicate(keepLocations, keepMetadata, keepDiagnostics), value = value.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -2294,9 +2313,10 @@ object IR { right = right.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -2379,9 +2399,10 @@ object IR { left = left.duplicate(keepLocations, keepMetadata, keepDiagnostics), right = right.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -2462,9 +2483,10 @@ object IR { left = left.duplicate(keepLocations, keepMetadata, keepDiagnostics), right = right.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -2544,9 +2566,10 @@ object IR { left = left.duplicate(keepLocations, keepMetadata, keepDiagnostics), right = right.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -2628,9 +2651,10 @@ object IR { right = right.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -2715,9 +2739,10 @@ object IR { right = right.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -2843,11 +2868,12 @@ object IR { arguments = arguments.map( _.duplicate(keepLocations, keepMetadata, keepDiagnostics) ), - body = body.duplicate(keepLocations, keepMetadata, keepDiagnostics), - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + body = body.duplicate(keepLocations, keepMetadata, keepDiagnostics), + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def setLocation(location: Option[IdentifiedLocation]): Lambda = @@ -2951,11 +2977,12 @@ object IR { arguments = arguments.map( _.duplicate(keepLocations, keepMetadata, keepDiagnostics) ), - body = body.duplicate(keepLocations, keepMetadata, keepDiagnostics), - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + body = body.duplicate(keepLocations, keepMetadata, keepDiagnostics), + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def setLocation(location: Option[IdentifiedLocation]): Binding = @@ -3092,10 +3119,11 @@ object IR { defaultValue = defaultValue.map( _.duplicate(keepLocations, keepMetadata, keepDiagnostics) ), - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def setLocation( @@ -3212,10 +3240,11 @@ object IR { arguments = arguments.map( _.duplicate(keepLocations, keepMetadata, keepDiagnostics) ), - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def setLocation(location: Option[IdentifiedLocation]): Prefix = @@ -3289,11 +3318,12 @@ object IR { keepMetadata: Boolean = true, keepDiagnostics: Boolean = true ): Force = copy( - target = target.duplicate(keepLocations, keepMetadata, keepDiagnostics), - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + target = target.duplicate(keepLocations, keepMetadata, keepDiagnostics), + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def setLocation(location: Option[IdentifiedLocation]): Force = @@ -3384,9 +3414,10 @@ object IR { _.duplicate(keepLocations, keepMetadata, keepDiagnostics) ), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -3465,9 +3496,10 @@ object IR { _.duplicate(keepLocations, keepMetadata, keepDiagnostics) ), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -3563,9 +3595,10 @@ object IR { operator.duplicate(keepLocations, keepMetadata, keepDiagnostics), right = right.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -3660,9 +3693,10 @@ object IR { operator = operator.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -3738,9 +3772,10 @@ object IR { operator = operator.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -3818,9 +3853,10 @@ object IR { operator.duplicate(keepLocations, keepMetadata, keepDiagnostics), arg = arg.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -3950,11 +3986,12 @@ object IR { ): Specified = copy( name = name.map(_.duplicate(keepLocations, keepMetadata, keepDiagnostics)), - value = value.duplicate(keepLocations, keepMetadata, keepDiagnostics), - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + value = value.duplicate(keepLocations, keepMetadata, keepDiagnostics), + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def setLocation( @@ -4053,12 +4090,14 @@ object IR { ): Expr = copy( scrutinee = scrutinee.duplicate(keepLocations, keepMetadata, keepDiagnostics), - branches = - branches.map(_.duplicate(keepLocations, keepMetadata, keepDiagnostics)), - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + branches = branches.map( + _.duplicate(keepLocations, keepMetadata, keepDiagnostics) + ), + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def setLocation(location: Option[IdentifiedLocation]): Expr = @@ -4146,10 +4185,11 @@ object IR { pattern.duplicate(keepLocations, keepMetadata, keepDiagnostics), expression = expression.duplicate(keepLocations, keepMetadata, keepDiagnostics), - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def setLocation(location: Option[IdentifiedLocation]): Branch = @@ -4244,11 +4284,12 @@ object IR { keepMetadata: Boolean = true, keepDiagnostics: Boolean = true ): Name = copy( - name = name.duplicate(keepLocations, keepMetadata, keepDiagnostics), - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + name = name.duplicate(keepLocations, keepMetadata, keepDiagnostics), + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def mapExpressions(fn: Expression => Expression): Name = { @@ -4327,10 +4368,11 @@ object IR { constructor.duplicate(keepLocations, keepMetadata, keepDiagnostics), fields = fields.map(_.duplicate(keepLocations, keepMetadata, keepDiagnostics)), - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) /** Checks if the constructor pattern has been desugared. @@ -4457,9 +4499,10 @@ object IR { ): Documentation = copy( location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -4549,10 +4592,11 @@ object IR { keepMetadata: Boolean = true, keepDiagnostics: Boolean = true ): Definition = copy( - location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + location = if (keepLocations) location else None, + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def setLocation( @@ -4749,6 +4793,7 @@ object IR { ) extends Error with Diagnostic.Kind.Interactive with IR.Module.Scope.Definition + with IR.Module.Scope.Import with IRKind.Primitive { override protected var id: Identifier = randomId @@ -4779,9 +4824,10 @@ object IR { keepDiagnostics: Boolean = true ): Syntax = copy( - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -4834,6 +4880,11 @@ object IR { s"Cannot define a pattern outside a pattern context." } + case object InvalidImport extends Reason { + override def explanation: String = + s"Imports must have a valid module path." + } + case object InvalidStandaloneSignature extends Reason { override def explanation: String = s"Invalid stand-alone signature expression." @@ -4938,10 +4989,11 @@ object IR { keepMetadata: Boolean = true, keepDiagnostics: Boolean = true ): InvalidIR = copy( - ir = ir.duplicate(keepLocations, keepMetadata, keepDiagnostics), - passData = if (keepMetadata) passData else MetadataStorage(), - diagnostics = if (keepDiagnostics) diagnostics else DiagnosticStorage(), - id = randomId + ir = ir.duplicate(keepLocations, keepMetadata, keepDiagnostics), + passData = if (keepMetadata) passData.duplicate else MetadataStorage(), + diagnostics = + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), + id = randomId ) override def setLocation( @@ -5027,9 +5079,10 @@ object IR { keepDiagnostics: Boolean = true ): ThisArg = copy( location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -5106,9 +5159,10 @@ object IR { methodName = methodName.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -5190,9 +5244,10 @@ object IR { atomName = atomName.duplicate(keepLocations, keepMetadata, keepDiagnostics), location = if (keepLocations) location else None, - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -5266,9 +5321,10 @@ object IR { ): Binding = copy( invalidBinding = invalidBinding .duplicate(keepLocations, keepMetadata, keepDiagnostics), - passData = if (keepMetadata) passData else MetadataStorage(), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -5376,10 +5432,11 @@ object IR { keepMetadata: Boolean = true, keepDiagnostics: Boolean = true ): TypeSignature = copy( - ir = ir.duplicate(keepLocations, keepMetadata, keepDiagnostics), - passData = if (keepMetadata) passData else MetadataStorage(), + ir = ir.duplicate(keepLocations, keepMetadata, keepDiagnostics), + passData = + if (keepMetadata) passData.duplicate else MetadataStorage(), diagnostics = - if (keepDiagnostics) diagnostics else DiagnosticStorage(), + if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(), id = randomId ) @@ -5463,130 +5520,6 @@ object IR { } } - // ========================================================================== - // === Diagnostics Storage ================================================== - // ========================================================================== - - /** Storage for diagnostics in IR nodes. - * - * @param initDiagnostics the initial diagnostics - */ - sealed class DiagnosticStorage(initDiagnostics: Seq[Diagnostic] = Seq()) { - private var diagnostics: List[Diagnostic] = initDiagnostics.toList - - /** Adds a new diagnostic to the storage - * - * @param diagnostic the new diagnostic to store - */ - def add(diagnostic: Diagnostic): Unit = { - diagnostics = diagnostic :: diagnostics - } - - /** Adds new diagnostics to the storage. - * - * @param newDiagnostics the new diagnostics to store - */ - def add(newDiagnostics: Seq[Diagnostic]): Unit = { - diagnostics = newDiagnostics.toList ::: diagnostics - } - - /** Applies the function `f` across the diagnostic storage, producing a - * result sequence. - * - * @param f the function to apply - * @tparam R the result type of `f` - * @return the sequence that results from applying `f` over the storage - */ - def map[R](f: IR.Diagnostic => R): Seq[R] = { - diagnostics.map(f) - } - - /** Applies the function `f` across the diagnostic storage in place. - * - * @param f the function to apply - */ - def mapInPlace(f: IR.Diagnostic => IR.Diagnostic): Unit = { - diagnostics = diagnostics.map(f) - } - - /** Performs a collection operation on the diagnostics storage, producing - * a new sequence. - * - * @param pf the partial function to apply - * @tparam R the result type of the partial function - * @return the result of collecting across the storage with `pf` - */ - def collect[R](pf: PartialFunction[IR.Diagnostic, R]): Seq[R] = { - diagnostics.collect(pf) - } - - /** Filters the elements of the diagnostic storage using the predicate. - * - * @param pred the predicate to filter with - * @return a new diagnostic storage instance containing elements matching - * `pred` - */ - def filter(pred: IR.Diagnostic => Boolean): DiagnosticStorage = { - new DiagnosticStorage(diagnostics.filter(pred)) - } - - /** Filters the elements of the diagnostic storage in place using the - * predicate. - * - * @param pred the predicate to filter with - */ - def filterInPlace(pred: IR.Diagnostic => Boolean): Unit = { - diagnostics = diagnostics.filter(pred) - } - - /** Performs a left fold over the diagnostic storage to produce a result. - * - * @param init the starting value - * @param op the operator to use to fold - * @tparam L the result type of the fold - * @return the result of folding over the storage using `op` starting wit - * `init` - */ - def foldLeft[L](init: L)(op: (L, IR.Diagnostic) => L): L = { - diagnostics.foldLeft(init)(op) - } - - /** Checks two diagnostics storages for equality. - * - * @param obj the object to check against `this` - * @return `true` if `this == obj`, otherwise `false` - */ - override def equals(obj: Any): Boolean = obj match { - case that: DiagnosticStorage => this.diagnostics == that.diagnostics - case _ => false - } - - /** Creates a string representation of `this` diagnostic storage. - * - * @return the string representation of `this` - */ - override def toString: String = - s"DiagnosticStorage(diagnostics = $diagnostics)" - - /** Creates a list of the diagnostics contained in the diagnostics storage. - * - * @return a list of the diagnostics in the storage - */ - def toList: List[IR.Diagnostic] = { - diagnostics - } - } - object DiagnosticStorage { - - /** Creates a new instance of the diagnostics storage. - * - * @param initDiagnostics the initial diagnostics to construct it with - * @return a new diagnostics storage instance - */ - def apply(initDiagnostics: Seq[Diagnostic] = Seq()): DiagnosticStorage = - new DiagnosticStorage(initDiagnostics) - } - // ========================================================================== // === Useful Extension Methods ============================================= // ========================================================================== @@ -5658,12 +5591,12 @@ object IR { /** Adds extension methods for working with lists of [[IR]]. * - * @param sequence the list + * @param list the list * @tparam T the concrete IR type */ - implicit class ListAsIr[T <: IR](sequence: List[T]) { + implicit class ListAsIr[T <: IR](list: List[T]) { - /** Calls [[IR.duplicate]] on the elemtsn in [[sequence]]. + /** Calls [[IR.duplicate]] on the elements in [[list]]. * * @param keepLocations whether or not locations should be kept in the * duplicated IR @@ -5671,14 +5604,14 @@ object IR { * the duplicated IR * @param keepDiagnostics whether or not the diagnostics should be kept in * the duplicated IR - * @return a duplicate of [[sequence]] + * @return a duplicate of [[list]] */ def duplicate( keepLocations: Boolean = true, keepMetadata: Boolean = true, keepDiagnostics: Boolean = true ): List[T] = { - sequence + list .map(_.duplicate(keepLocations, keepMetadata, keepDiagnostics)) .asInstanceOf[List[T]] } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/core/ir/DiagnosticStorage.scala b/engine/runtime/src/main/scala/org/enso/compiler/core/ir/DiagnosticStorage.scala new file mode 100644 index 00000000000..c70842e350f --- /dev/null +++ b/engine/runtime/src/main/scala/org/enso/compiler/core/ir/DiagnosticStorage.scala @@ -0,0 +1,134 @@ +package org.enso.compiler.core.ir + +import org.enso.compiler.core.IR +import org.enso.compiler.core.IR.Diagnostic + +/** Storage for diagnostics in IR nodes. + * + * @param initDiagnostics the initial diagnostics + */ +sealed class DiagnosticStorage(initDiagnostics: Seq[Diagnostic] = Seq()) { + private var diagnostics: List[Diagnostic] = initDiagnostics.toList + + /** Adds a new diagnostic to the storage + * + * @param diagnostic the new diagnostic to store + */ + def add(diagnostic: Diagnostic): Unit = { + diagnostics = diagnostic :: diagnostics + } + /** Adds new diagnostics to the storage. + * + * @param newDiagnostics the new diagnostics to store + */ + def add(newDiagnostics: Seq[Diagnostic]): Unit = { + diagnostics = newDiagnostics.toList ::: diagnostics + } + + /** Applies the function `f` across the diagnostic storage, producing a + * result sequence. + * + * @param f the function to apply + * @tparam R the result type of `f` + * @return the sequence that results from applying `f` over the storage + */ + def map[R](f: IR.Diagnostic => R): Seq[R] = { + diagnostics.map(f) + } + + /** Applies the function `f` across the diagnostic storage in place. + * + * @param f the function to apply + */ + def mapInPlace(f: IR.Diagnostic => IR.Diagnostic): Unit = { + diagnostics = diagnostics.map(f) + } + + /** Performs a collection operation on the diagnostics storage, producing + * a new sequence. + * + * @param pf the partial function to apply + * @tparam R the result type of the partial function + * @return the result of collecting across the storage with `pf` + */ + def collect[R](pf: PartialFunction[IR.Diagnostic, R]): Seq[R] = { + diagnostics.collect(pf) + } + + /** Filters the elements of the diagnostic storage using the predicate. + * + * @param pred the predicate to filter with + * @return a new diagnostic storage instance containing elements matching + * `pred` + */ + def filter(pred: IR.Diagnostic => Boolean): DiagnosticStorage = { + new DiagnosticStorage(diagnostics.filter(pred)) + } + + /** Filters the elements of the diagnostic storage in place using the + * predicate. + * + * @param pred the predicate to filter with + */ + def filterInPlace(pred: IR.Diagnostic => Boolean): Unit = { + diagnostics = diagnostics.filter(pred) + } + + /** Performs a left fold over the diagnostic storage to produce a result. + * + * @param init the starting value + * @param op the operator to use to fold + * @tparam L the result type of the fold + * @return the result of folding over the storage using `op` starting wit + * `init` + */ + def foldLeft[L](init: L)(op: (L, IR.Diagnostic) => L): L = { + diagnostics.foldLeft(init)(op) + } + + /** Checks two diagnostics storages for equality. + * + * @param obj the object to check against `this` + * @return `true` if `this == obj`, otherwise `false` + */ + override def equals(obj: Any): Boolean = obj match { + case that: DiagnosticStorage => this.diagnostics == that.diagnostics + case _ => false + } + + /** Creates a string representation of `this` diagnostic storage. + * + * @return the string representation of `this` + */ + override def toString: String = + s"DiagnosticStorage(diagnostics = $diagnostics)" + + /** Creates a list of the diagnostics contained in the diagnostics storage. + * + * @return a list of the diagnostics in the storage + */ + def toList: List[IR.Diagnostic] = { + diagnostics + } + + /** Creates a shallow copy of `this`. + * + * This means that the diagnostic objects contained in `this` and the copy + * are the same objects. + * + * @return a shallow copy of this + */ + def copy: DiagnosticStorage = { + DiagnosticStorage(this.diagnostics) + } +} +object DiagnosticStorage { + + /** Creates a new instance of the diagnostics storage. + * + * @param initDiagnostics the initial diagnostics to construct it with + * @return a new diagnostics storage instance + */ + def apply(initDiagnostics: Seq[Diagnostic] = Seq()): DiagnosticStorage = + new DiagnosticStorage(initDiagnostics) +} diff --git a/engine/runtime/src/main/scala/org/enso/compiler/core/ir/MetadataStorage.scala b/engine/runtime/src/main/scala/org/enso/compiler/core/ir/MetadataStorage.scala index 5b9892408b5..27c3a9bda99 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/core/ir/MetadataStorage.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/core/ir/MetadataStorage.scala @@ -13,9 +13,9 @@ import org.enso.compiler.pass.IRPass class MetadataStorage( startingMeta: Seq[MetadataPair[_]] = Seq() ) { - private val pairs: Seq[(IRPass, Any)] = - startingMeta.map(_.asPair.asInstanceOf[(IRPass, Any)]) - private var metadata: Map[IRPass, Any] = Map(pairs: _*) + private var metadata: Map[IRPass, Any] = Map( + startingMeta.map(_.asPair.asInstanceOf[(IRPass, Any)]): _* + ) /** Adds a metadata pair to the node metadata. * @@ -112,6 +112,19 @@ class MetadataStorage( } override def toString: String = metadata.toString() + + /** Creates a deep copy of `this`. + * + * @return a deep copy of `this` + */ + def duplicate: MetadataStorage = { + val res = MetadataStorage() + res.metadata = this.metadata.map { + case (pass, meta) => (pass, meta.asInstanceOf[IRPass.Metadata].duplicate) + } + + res + } } object MetadataStorage extends MetadataStorageSyntax { diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/IRPass.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/IRPass.scala index 48d22059dc8..096e374d7b8 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/IRPass.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/IRPass.scala @@ -128,12 +128,22 @@ object IRPass { throw new CompilerError(s"Cannot cast $this to the requested type.") ) } + + /** Creates a duplicate of this metadata. + * + * This method should employ deep-copy semantics where appropriate. + * + * @return a duplicate of this metadata. + */ + def duplicate: Metadata } object Metadata { /** An empty metadata type for passes that do not create any metadata. */ sealed case class Empty() extends Metadata { override val metadataName: String = "Empty" + + override def duplicate: Empty = Empty() } } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/AliasAnalysis.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/AliasAnalysis.scala index 42c2a05c1c5..c5238c83ce7 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/AliasAnalysis.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/AliasAnalysis.scala @@ -608,6 +608,8 @@ case object AliasAnalysis extends IRPass { */ sealed case class Root(override val graph: Graph) extends Scope { override val metadataName: String = "AliasAnalysis.Info.Scope.Root" + + override def duplicate: IRPass.Metadata = this.copy() } /** Aliasing information about a child scope. @@ -618,6 +620,8 @@ case object AliasAnalysis extends IRPass { sealed case class Child(override val graph: Graph, scope: Graph.Scope) extends Scope { override val metadataName: String = "AliasAnalysis.Info.Scope.Child" + + override def duplicate: IRPass.Metadata = this.copy() } } @@ -630,6 +634,8 @@ case object AliasAnalysis extends IRPass { sealed case class Occurrence(override val graph: Graph, id: Graph.Id) extends Info { override val metadataName: String = "AliasAnalysis.Info.Occurrence" + + override def duplicate: IRPass.Metadata = this.copy() } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/CachePreferenceAnalysis.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/CachePreferenceAnalysis.scala index 2ad3ee86111..c6318fe8fc6 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/CachePreferenceAnalysis.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/CachePreferenceAnalysis.scala @@ -203,6 +203,8 @@ case object CachePreferenceAnalysis extends IRPass { /** @return weights as the Java collection */ def asJavaWeights: util.Map[IR.ExternalId, java.lang.Double] = weights.asJava.asInstanceOf[util.Map[IR.ExternalId, java.lang.Double]] + + override def duplicate: IRPass.Metadata = copy() } /** Weight constants */ diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DataflowAnalysis.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DataflowAnalysis.scala index e011f7040f0..3d9b6d2d51f 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DataflowAnalysis.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/DataflowAnalysis.scala @@ -726,6 +726,8 @@ case object DataflowAnalysis extends IRPass { combinedModule } + + override def duplicate: IRPass.Metadata = copy() } object DependencyInfo { diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/GatherDiagnostics.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/GatherDiagnostics.scala index adf07bddb39..d98d0ac9ea1 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/GatherDiagnostics.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/GatherDiagnostics.scala @@ -68,5 +68,8 @@ case object GatherDiagnostics extends IRPass { /** The name of the metadata as a string. */ override val metadataName: String = "GatherDiagnostics.Diagnostics" + + override def duplicate: IRPass.Metadata = + this.copy(diagnostics.map(identity)) } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailCall.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailCall.scala index bfeebb39c16..2ec7dc5d001 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailCall.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailCall.scala @@ -423,6 +423,8 @@ case object TailCall extends IRPass { final case object Tail extends TailPosition { override val metadataName: String = "TailCall.TailPosition.Tail" override def isTail: Boolean = true + + override def duplicate: IRPass.Metadata = Tail } /** The expression is not in a tail position and cannot be tail call @@ -431,6 +433,8 @@ case object TailCall extends IRPass { final case object NotTail extends TailPosition { override val metadataName: String = "TailCall.TailPosition.NotTail" override def isTail: Boolean = false + + override def duplicate: IRPass.Metadata = NotTail } /** Implicitly converts a boolean to a [[TailPosition]] value. diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/ComplexType.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/ComplexType.scala index 0577b22c175..c6f2f7ae7a1 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/ComplexType.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/ComplexType.scala @@ -237,18 +237,12 @@ case object ComplexType extends IRPass { ) val newSig = - signature.map(sig => - sig - .copy(typed = - methodRef.duplicate(keepMetadata = false, keepDiagnostics = false) - ) - .duplicate(keepMetadata = false, keepDiagnostics = false) - ) + signature.map(sig => sig.copy(typed = methodRef.duplicate()).duplicate()) val binding = Method.Binding( - methodRef.duplicate(keepMetadata = false, keepDiagnostics = false), + methodRef.duplicate(), args, - body.duplicate(keepMetadata = false, keepDiagnostics = false), + body.duplicate(), location ) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala index 6d2424d68b3..b2e1525383d 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala @@ -180,10 +180,7 @@ case object NestedPatternMatch extends IRPass { val scrutineeBinding = IR.Expression.Binding(scrutineeBindingName, scrutineeExpression, None) - val caseExprScrutinee = scrutineeBindingName.duplicate( - keepDiagnostics = false, - keepMetadata = false - ) + val caseExprScrutinee = scrutineeBindingName.duplicate() val processedBranches = branches.zipWithIndex.map { case (branch, ix) => @@ -240,7 +237,8 @@ case object NestedPatternMatch extends IRPass { val newName = freshNameSupply.newName() val newField = Pattern.Name(newName, None) val nestedScrutinee = - newName.duplicate(keepDiagnostics = false, keepMetadata = false) + newName.duplicate() + newName.duplicate() val newFields = fields.take(nestedPosition) ++ (newField :: fields.drop( @@ -249,7 +247,7 @@ case object NestedPatternMatch extends IRPass { val newPattern = cons.copy( fields = - newFields.duplicate(keepDiagnostics = false, keepMetadata = false) + newFields.duplicate() ) val newExpression = generateNestedCase( @@ -261,10 +259,8 @@ case object NestedPatternMatch extends IRPass { ) val partDesugaredBranch = IR.Case.Branch( - pattern = newPattern - .duplicate(keepDiagnostics = false, keepMetadata = false), - expression = newExpression - .duplicate(keepDiagnostics = false, keepMetadata = false), + pattern = newPattern.duplicate(), + expression = newExpression.duplicate(), None ) @@ -321,15 +317,15 @@ case object NestedPatternMatch extends IRPass { remainingBranches: List[IR.Case.Branch] ): IR.Expression = { val fallbackCase = IR.Case.Expr( - topLevelScrutineeExpr.duplicate(keepDiagnostics = false), - remainingBranches.duplicate(keepMetadata = false), + topLevelScrutineeExpr.duplicate(), + remainingBranches.duplicate(), None ) val patternBranch = IR.Case.Branch( - pattern.duplicate(keepDiagnostics = false), - currentBranchExpr.duplicate(keepMetadata = false), + pattern.duplicate(), + currentBranchExpr.duplicate(), None ) val fallbackBranch = IR.Case.Branch( @@ -339,7 +335,7 @@ case object NestedPatternMatch extends IRPass { ) IR.Case.Expr( - nestedScrutinee.duplicate(keepDiagnostics = false, keepMetadata = false), + nestedScrutinee.duplicate(), List(patternBranch, fallbackBranch), None ) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/SectionsToBinOp.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/SectionsToBinOp.scala index e22813ea6f5..332de29df5a 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/SectionsToBinOp.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/SectionsToBinOp.scala @@ -96,11 +96,7 @@ case object SectionsToBinOp extends IRPass { val rightCallArg = IR.CallArgument.Specified(None, rightArgName, None, None) val rightDefArg = IR.DefinitionArgument.Specified( - rightArgName.duplicate( - keepLocations = false, - keepDiagnostics = false, - keepMetadata = false - ), + rightArgName.duplicate(), None, suspended = false, None @@ -111,11 +107,7 @@ case object SectionsToBinOp extends IRPass { val leftCallArg = IR.CallArgument.Specified(None, leftArgName, None, None) val leftDefArg = IR.DefinitionArgument.Specified( - leftArgName.duplicate( - keepLocations = false, - keepDiagnostics = false, - keepMetadata = false - ), + leftArgName.duplicate(), None, suspended = false, None @@ -162,11 +154,7 @@ case object SectionsToBinOp extends IRPass { val leftCallArg = IR.CallArgument.Specified(None, leftArgName, None, None) val leftDefArg = IR.DefinitionArgument.Specified( - leftArgName.duplicate( - keepLocations = false, - keepDiagnostics = false, - keepMetadata = false - ), + leftArgName.duplicate(), None, suspended = false, None @@ -176,11 +164,7 @@ case object SectionsToBinOp extends IRPass { val rightCallArg = IR.CallArgument.Specified(None, rightArgName, None, None) val rightDefArg = IR.DefinitionArgument.Specified( - rightArgName.duplicate( - keepLocations = false, - keepDiagnostics = false, - keepMetadata = false - ), + rightArgName.duplicate(), None, suspended = false, None @@ -232,11 +216,7 @@ case object SectionsToBinOp extends IRPass { IR.CallArgument.Specified(None, leftArgName, None, None) val leftDefArg = IR.DefinitionArgument.Specified( - leftArgName.duplicate( - keepLocations = false, - keepMetadata = false, - keepDiagnostics = false - ), + leftArgName.duplicate(), None, suspended = false, None @@ -248,11 +228,7 @@ case object SectionsToBinOp extends IRPass { val rightCallArg = IR.CallArgument.Specified(None, rightArgName, None, None) val rightDefArg = IR.DefinitionArgument.Specified( - rightArgName.duplicate( - keepLocations = false, - keepMetadata = false, - keepDiagnostics = false - ), + rightArgName.duplicate(), None, suspended = false, None diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/optimise/ApplicationSaturation.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/optimise/ApplicationSaturation.scala index 58a59c74424..c35af62aad6 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/optimise/ApplicationSaturation.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/optimise/ApplicationSaturation.scala @@ -197,22 +197,32 @@ case object ApplicationSaturation extends IRPass { sealed case class Over(additionalArgCount: Int) extends CallSaturation { override val metadataName: String = "ApplicationSaturation.CallSaturation.Over" + + override def duplicate: IRPass.Metadata = copy() } sealed case class Exact(helper: CodegenHelper) extends CallSaturation { override val metadataName: String = "ApplicationSaturation.CallSaturation.Exact" + + override def duplicate: IRPass.Metadata = copy() } sealed case class ExactButByName() extends CallSaturation { override val metadataName: String = "ApplicationSaturation.CallSaturation.ExactButByName" + + override def duplicate: IRPass.Metadata = ExactButByName() } sealed case class Partial(unappliedArgCount: Int) extends CallSaturation { override val metadataName: String = "ApplicationSaturation.CallSaturation.Partial" + + override def duplicate: IRPass.Metadata = copy() } sealed case class Unknown() extends CallSaturation { override val metadataName: String = "ApplicationSaturation.CallSaturation.Unknown" + + override def duplicate: IRPass.Metadata = Unknown() } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/DocumentationComments.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/DocumentationComments.scala index bbc1bf448cd..f1c592ada4f 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/DocumentationComments.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/DocumentationComments.scala @@ -142,5 +142,7 @@ case object DocumentationComments extends IRPass { */ sealed case class Doc(documentation: String) extends IRPass.Metadata { override val metadataName: String = "DocumentationComments.Doc" + + override def duplicate: IRPass.Metadata = copy(documentation.map(identity)) } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/IgnoredBindings.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/IgnoredBindings.scala index cae21f985be..fd785974e60 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/IgnoredBindings.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/IgnoredBindings.scala @@ -324,12 +324,16 @@ case object IgnoredBindings extends IRPass { case object Ignored extends State { override val metadataName: String = "IgnoredBindings.State.Ignored" override val isIgnored: Boolean = true + + override def duplicate: IRPass.Metadata = Ignored } /** States that the binding is not ignored. */ case object NotIgnored extends State { override val metadataName: String = "IgnoredBindings.State.NotIgnored" override val isIgnored: Boolean = false + + override def duplicate: IRPass.Metadata = NotIgnored } } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/TypeSignatures.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/TypeSignatures.scala index 7eed229965a..89412fe1197 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/TypeSignatures.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/TypeSignatures.scala @@ -205,5 +205,8 @@ case object TypeSignatures extends IRPass { */ case class Signature(signature: IR.Expression) extends IRPass.Metadata { override val metadataName: String = "TypeSignatures.Signature" + + override def duplicate: IRPass.Metadata = + this.copy(signature = signature.duplicate()) } } diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/core/IRTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/core/ir/DiagnosticStorageTest.scala similarity index 86% rename from engine/runtime/src/test/scala/org/enso/compiler/test/core/IRTest.scala rename to engine/runtime/src/test/scala/org/enso/compiler/test/core/ir/DiagnosticStorageTest.scala index c5a46afb31b..3c3f535f046 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/core/IRTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/core/ir/DiagnosticStorageTest.scala @@ -1,11 +1,11 @@ -package org.enso.compiler.test.core +package org.enso.compiler.test.core.ir -import org.enso.compiler.core import org.enso.compiler.core.IR +import org.enso.compiler.core.ir.DiagnosticStorage import org.enso.compiler.test.CompilerTest import org.enso.syntax.text.AST -class IRTest extends CompilerTest { +class DiagnosticStorageTest extends CompilerTest { // === Test Configuration =================================================== @@ -22,14 +22,14 @@ class IRTest extends CompilerTest { "The IR diagnostics storage" should { "allow adding diagnostic results" in { - val diagnostics = new IR.DiagnosticStorage + val diagnostics = new DiagnosticStorage diagnostics.add(mkDiagnostic("a")) diagnostics.toList should contain(mkDiagnostic("a")) } "allow adding lists of diagnostic results" in { - val diagnostics = new IR.DiagnosticStorage + val diagnostics = new DiagnosticStorage diagnostics.add( List( @@ -44,7 +44,7 @@ class IRTest extends CompilerTest { } "mapping across the diagnostics to produce a new sequence" in { - val diagnostics = new IR.DiagnosticStorage( + val diagnostics = new DiagnosticStorage( List( mkDiagnostic("a"), mkDiagnostic("b"), @@ -56,7 +56,7 @@ class IRTest extends CompilerTest { } "mapping across the diagnostics in place" in { - val diagnostics = new IR.DiagnosticStorage( + val diagnostics = new DiagnosticStorage( List( mkDiagnostic("a"), mkDiagnostic("b"), @@ -83,7 +83,7 @@ class IRTest extends CompilerTest { val err = IR.Error.Syntax(AST.Blank(), IR.Error.Syntax.UnsupportedSyntax("aa")) - val diagnostics = new IR.DiagnosticStorage( + val diagnostics = new DiagnosticStorage( List( mkDiagnostic("a"), mkDiagnostic("b"), @@ -98,7 +98,7 @@ class IRTest extends CompilerTest { } "filtering the diagnostics" in { - val diagnostics = new IR.DiagnosticStorage( + val diagnostics = new DiagnosticStorage( List( mkDiagnostic("aa"), mkDiagnostic("ba"), @@ -106,7 +106,7 @@ class IRTest extends CompilerTest { ) ) - val result = new core.IR.DiagnosticStorage( + val result = new DiagnosticStorage( List(mkDiagnostic("aa"), mkDiagnostic("ba")) ) @@ -118,7 +118,7 @@ class IRTest extends CompilerTest { } "filtering the diagnostics in place" in { - val diagnostics = new IR.DiagnosticStorage( + val diagnostics = new DiagnosticStorage( List( mkDiagnostic("aa"), mkDiagnostic("ba"), @@ -138,7 +138,7 @@ class IRTest extends CompilerTest { } "folding over the diagnostics" in { - val diagnostics = new IR.DiagnosticStorage( + val diagnostics = new DiagnosticStorage( List( mkDiagnostic("a"), mkDiagnostic("b"), diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/core/ir/MetadataStorageTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/core/ir/MetadataStorageTest.scala index e65298595f9..391d113aceb 100644 --- a/engine/runtime/src/test/scala/org/enso/compiler/test/core/ir/MetadataStorageTest.scala +++ b/engine/runtime/src/test/scala/org/enso/compiler/test/core/ir/MetadataStorageTest.scala @@ -32,6 +32,8 @@ class MetadataStorageTest extends CompilerTest { sealed case class Metadata1() extends IRPass.Metadata { override val metadataName: String = "TestPass1.Metadata1" + + override def duplicate: IRPass.Metadata = Metadata1() } } @@ -54,6 +56,8 @@ class MetadataStorageTest extends CompilerTest { sealed case class Metadata2() extends IRPass.Metadata { override val metadataName: String = "TestPass2.Metadata2" + + override def duplicate: IRPass.Metadata = Metadata2() } } diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala index 5775f34a104..4ffd0c729dd 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala @@ -47,7 +47,7 @@ class RuntimeServerTest .allowExperimentalOptions(true) .allowAllAccess(true) .option(RuntimeOptions.PACKAGES_PATH, pkg.root.getAbsolutePath) - .option(RuntimeOptions.LOG_LEVEL, "FINE") + .option(RuntimeOptions.LOG_LEVEL, "WARNING") .option(RuntimeServerInfo.ENABLE_OPTION, "true") .out(out) .serverTransport { (uri, peer) => diff --git a/lib/syntax/definition/src/main/scala/org/enso/syntax/text/AST.scala b/lib/syntax/definition/src/main/scala/org/enso/syntax/text/AST.scala index f6428e1e6c7..8537eb843b4 100644 --- a/lib/syntax/definition/src/main/scala/org/enso/syntax/text/AST.scala +++ b/lib/syntax/definition/src/main/scala/org/enso/syntax/text/AST.scala @@ -339,7 +339,7 @@ object Shape extends ShapeImplicit { with Phantom final case class Documented[T](doc: Doc, emptyLinesBetween: Int, ast: T) extends SpacelessAST[T] - final case class Import[T](path: List1[AST.Cons]) extends SpacelessAST[T] + final case class Import[T](path: AST) extends SpacelessAST[T] final case class JavaImport[T](path: List1[AST.Ident]) extends SpacelessAST[T] final case class Mixfix[T](name: List1[AST.Ident], args: List1[T]) extends SpacelessAST[T] @@ -351,6 +351,8 @@ object Shape extends ShapeImplicit { extends SpacelessAST[T] final case class Foreign[T](indent: Int, lang: String, code: List[String]) extends SpacelessAST[T] + final case class Modified[T](modifier: String, definition: T) + extends SpacelessAST[T] ////////////////////////////////////////////////////////////////////////////// // Companion objects ///////////////////////////////////////////////////////// @@ -954,7 +956,7 @@ object Shape extends ShapeImplicit { implicit def ftor: Functor[Import] = semi.functor implicit def fold: Foldable[Import] = semi.foldable implicit def repr[T]: Repr[Import[T]] = - t => R + ("import " + t.path.map(_.repr.build()).toList.mkString(".")) + t => R + "import" + t.path.repr.build() // FIXME: How to make it automatic for non-spaced AST? implicit def ozip[T]: OffsetZip[Import, T] = _.map(Index.Start -> _) @@ -1043,6 +1045,18 @@ object Shape extends ShapeImplicit { implicit def ozip[T]: OffsetZip[Foreign, T] = _.map(Index.Start -> _) implicit def span[T]: HasSpan[Foreign[T]] = _ => 0 } + + object Modified { + implicit def ftor: Functor[Modified] = semi.functor + implicit def fold: Foldable[Modified] = semi.foldable + implicit def repr[T: Repr]: Repr[Modified[T]] = t => { + R + t.modifier + t.definition.repr.build() + } + // FIXME: How to make it automatic for non-spaced AST? + implicit def ozip[T]: OffsetZip[Modified, T] = _.map(Index.Start -> _) + implicit def span[T]: HasSpan[Modified[T]] = _ => 0 + } + //// Implicits //// object implicits { @@ -1117,6 +1131,7 @@ sealed trait ShapeImplicit { case s: TypesetLiteral[T] => s.repr case s: Def[T] => s.repr case s: Foreign[T] => s.repr + case s: Modified[T] => s.repr } implicit def ozip[T: HasSpan]: OffsetZip[Shape, T] = { case s: Unrecognized[T] => OffsetZip[Unrecognized, T].zipWithOffset(s) @@ -1156,6 +1171,7 @@ sealed trait ShapeImplicit { case s: TypesetLiteral[T] => OffsetZip[TypesetLiteral, T].zipWithOffset(s) case s: Def[T] => OffsetZip[Def, T].zipWithOffset(s) case s: Foreign[T] => OffsetZip[Foreign, T].zipWithOffset(s) + case s: Modified[T] => OffsetZip[Modified, T].zipWithOffset(s) } implicit def span[T: HasSpan]: HasSpan[Shape[T]] = { @@ -1196,6 +1212,7 @@ sealed trait ShapeImplicit { case s: TypesetLiteral[T] => s.span() case s: Def[T] => s.span() case s: Foreign[T] => s.span() + case s: Modified[T] => s.span() } } @@ -2297,11 +2314,9 @@ object AST { type Import = ASTOf[Shape.Import] object Import { - def apply(path: List1[Cons]): Import = Shape.Import[AST](path) - def apply(head: Cons): Import = Import(head, List()) - def apply(head: Cons, tail: List[Cons]): Import = Import(List1(head, tail)) - def apply(head: Cons, tail: Cons*): Import = Import(head, tail.toList) - def unapply(t: AST): Option[List1[Cons]] = + def apply(path: AST): Import = + Shape.Import[AST](path) + def unapply(t: AST): Option[AST] = Unapply[Import].run(t => t.path)(t) val any = UnapplyByType[Import] } @@ -2399,6 +2414,25 @@ object AST { val any = UnapplyByType[Foreign] } + ////////////////////////////////////////////////////////////////////////////// + //// Modified //////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + type Modified = ASTOf[Shape.Modified] + object Modified { + def apply(modifier: String, definition: AST): Modified = { + Shape.Modified(modifier, definition) + } + def unapply(t: AST): Option[(String, AST)] = { + Unapply[Modified].run(t => (t.modifier, t.definition))(t) + } + val any = UnapplyByType[Modified] + } + + ////////////////////////////////////////////////////////////////////////////// + //// Main //////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + def main(): Unit = { val v1 = Ident.Var("foo") // val v1_ = v1: AST diff --git a/lib/syntax/definition/src/main/scala/org/enso/syntax/text/ast/meta/Builtin.scala b/lib/syntax/definition/src/main/scala/org/enso/syntax/text/ast/meta/Builtin.scala index 2893b0b2a6d..c1abf5a9e0a 100644 --- a/lib/syntax/definition/src/main/scala/org/enso/syntax/text/ast/meta/Builtin.scala +++ b/lib/syntax/definition/src/main/scala/org/enso/syntax/text/ast/meta/Builtin.scala @@ -1,18 +1,17 @@ package org.enso.syntax.text.ast.meta -import org.enso.data.List1 -import org.enso.data.Shifted +import org.enso.data.{List1, Shifted} import org.enso.syntax.text.AST import org.enso.syntax.text.AST.Macro.Definition -import org.enso.syntax.text.AST.Opr -import org.enso.syntax.text.AST.Var +import org.enso.syntax.text.AST.{Opr, Var} import org.enso.syntax.text.ast.Repr -import scala.annotation.tailrec +import scala.annotation.{nowarn, tailrec} /** It contains definitions of built-in macros, like if-then-else or (-). These * macros might get moved to stdlib in the future. */ +@nowarn("cat=unused") object Builtin { val registry: Registry = { @@ -119,34 +118,6 @@ object Builtin { } } - val imp = Definition( - Var("import") -> Pattern - .SepList(Pattern.Cons(), AST.Opr("."): AST, "expected module name") - ) { ctx => - ctx.body match { - case List(s1) => - import Pattern.Match._ - s1.body match { - case Seq(_, (headMatch, Many(_, tailMatch))) => - def unwrapSeg(lseg: Pattern.Match): AST.Cons = - lseg.toStream match { - case List(Shifted(_, AST.Cons.any(t))) => t - case _ => internalError - } - - val head = unwrapSeg(headMatch) - val tail = tailMatch.map { - case Seq(_, (Tok(_, Shifted(_, AST.Opr("."))), seg)) => - unwrapSeg(seg) - case _ => internalError - } - AST.Import(head, tail) - case _ => internalError - } - case _ => internalError - } - } - val if_then = Definition( Var("if") -> Pattern.Expr(allowBlocks = false), Var("then") -> Pattern.Expr() @@ -201,6 +172,8 @@ object Builtin { val nonSpacedExpr = Pattern.Any(Some(false)).many1.build + // NOTE: The macro engine currently resolves ahead of all operators, meaning + // that `->` doesn't obey the right precedence (e.g. with respect to `:`). val arrow = Definition( Some(nonSpacedExpr.or(Pattern.ExprUntilOpr("->"))), Opr("->") -> Pattern.NonSpacedExpr().or(Pattern.Expr()) @@ -259,7 +232,7 @@ object Builtin { } val freeze = Definition( - Var("freeze") -> Pattern.Expr() + Var("freeze") -> (Pattern.Var() :: Pattern.Block()) ) { ctx => ctx.body match { case List(s1) => @@ -293,6 +266,50 @@ object Builtin { } } + val `import` = { + Definition( + Var("import") -> Pattern.Expr() + ) { ctx => + ctx.body match { + case List(s1) => + s1.body.toStream match { + case List(expr) => + AST.Import(expr.wrapped) + case _ => internalError + } + case _ => internalError + } + } + } + + val privateDef = { + Definition(Var("private") -> Pattern.Expr()) { ctx => + ctx.body match { + case List(s1) => + s1.body.toStream match { + case List(expr) => + AST.Modified("private", expr.wrapped) + case _ => internalError + } + case _ => internalError + } + } + } + + val unsafeDef = { + Definition(Var("unsafe") -> Pattern.Expr()) { ctx => + ctx.body match { + case List(s1) => + s1.body.toStream match { + case List(expr) => + AST.Modified("unsafe", expr.wrapped) + case _ => internalError + } + case _ => internalError + } + } + } + // TODO // We may want to better represent empty AST. Moreover, there should be a // way to generate multiple top-level entities from macros (like multiple @@ -304,6 +321,8 @@ object Builtin { Definition(Opr("#") -> Pattern.Expr().tag("disable")) { _ => AST.Blank() } Registry( + privateDef, + unsafeDef, group, sequenceLiteral, typesetLiteral, @@ -311,7 +330,7 @@ object Builtin { if_then, if_then_else, polyglotJavaImport, - imp, + `import`, defn, arrow, foreign, diff --git a/lib/syntax/specialization/shared/src/test/scala/org/enso/syntax/text/ParserTest.scala b/lib/syntax/specialization/shared/src/test/scala/org/enso/syntax/text/ParserTest.scala index a302ce147ef..ac07f29d7d9 100644 --- a/lib/syntax/specialization/shared/src/test/scala/org/enso/syntax/text/ParserTest.scala +++ b/lib/syntax/specialization/shared/src/test/scala/org/enso/syntax/text/ParserTest.scala @@ -346,8 +346,6 @@ class ParserTest extends AnyFlatSpec with Matchers { "(" ?= amb("(", List(List(")"))) "((" ?= amb_group(group_()) - "import Std . Math .Vector".stripMargin ?= Import("Std", "Math", "Vector") - """type Maybe a | type Just val:a | type Nothing""".stripMargin ?= {