Export works without import (#9683)

This commit is contained in:
Pavel Marek 2024-04-12 14:23:34 +02:00 committed by GitHub
parent cdf031f61f
commit 4dc7992ab5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 205 additions and 269 deletions

View File

@ -1,9 +1,3 @@
import project.AWS_Credential.AWS_Credential
import project.AWS_Region.AWS_Region
import project.Database.Redshift.Redshift_Details.Redshift_Details
import project.S3.S3
import project.S3.S3_File.S3_File
export project.AWS_Credential.AWS_Credential
export project.AWS_Region.AWS_Region
export project.Database.Redshift.Redshift_Details.Redshift_Details

View File

@ -1,6 +1,5 @@
import project.Any.Any
import project.Nothing.Nothing
from project.Data.Boolean.Boolean import False, True
from project.Data.Boolean.Boolean export False, True

View File

@ -1,108 +1,3 @@
import project.Any.Any
import project.Data
import project.Data.Array.Array
import project.Data.Boolean
import project.Data.Decimal.Decimal
import project.Data.Filter_Condition.Filter_Action
import project.Data.Filter_Condition.Filter_Condition
import project.Data.Index_Sub_Range.Index_Sub_Range
import project.Data.Interval.Bound
import project.Data.Interval.Interval
import project.Data.Interval.Interval_Type
import project.Data.Json.JS_Object
import project.Data.Json.Json
import project.Data.List.List
import project.Data.Locale.Locale
import project.Data.Map.Map
import project.Data.Maybe.Maybe
import project.Data.Numbers
import project.Data.Numeric.Math_Context.Math_Context
import project.Data.Numeric.Math_Context.Unlimited
import project.Data.Numeric.Rounding_Mode.Rounding_Mode
import project.Data.Ordering.Comparable
import project.Data.Ordering.Default_Comparator
import project.Data.Ordering.Natural_Order
import project.Data.Ordering.Ordering
import project.Data.Pair.Pair
import project.Data.Range.Range
import project.Data.Regression
import project.Data.Set.Set
import project.Data.Sort_Direction.Sort_Direction
import project.Data.Statistics
import project.Data.Text.Case.Case
import project.Data.Text.Case_Sensitivity.Case_Sensitivity
import project.Data.Text.Encoding.Encoding
import project.Data.Text.Line_Ending_Style.Line_Ending_Style
import project.Data.Text.Location.Location
import project.Data.Text.Matching_Mode.Matching_Mode
import project.Data.Text.Normalization.Normalization
import project.Data.Text.Regex.Regex
import project.Data.Text.Text
import project.Data.Text.Text_Ordering.Text_Ordering
import project.Data.Text.Text_Sub_Range.Text_Sub_Range
import project.Data.Time.Date.Date
import project.Data.Time.Date_Period.Date_Period
import project.Data.Time.Date_Range.Date_Range
import project.Data.Time.Date_Time.Date_Time
import project.Data.Time.Date_Time_Formatter.Date_Time_Formatter
import project.Data.Time.Day_Of_Week.Day_Of_Week
import project.Data.Time.Duration.Duration
import project.Data.Time.Period.Period
import project.Data.Time.Time_Of_Day.Time_Of_Day
import project.Data.Time.Time_Period.Time_Period
import project.Data.Time.Time_Zone.Time_Zone
import project.Data.Vector.Vector
import project.Data.XML.XML_Document
import project.Data.XML.XML_Element
import project.Data.XML.XML_Error
import project.Data.XML.XML_Format.XML_Format
import project.Enso_Cloud.Enso_File.Enso_File
import project.Enso_Cloud.Enso_Secret.Enso_Secret
import project.Enso_Cloud.Enso_User.Enso_User
import project.Error.Error
import project.Errors
import project.Errors.Problem_Behavior.Problem_Behavior
import project.Function
import project.IO
import project.Math
import project.Meta
import project.Meta.Enso_Project.Project_Description
import project.Network.HTTP.Header.Header
import project.Network.HTTP.HTTP
import project.Network.HTTP.HTTP_Method.HTTP_Method
import project.Network.HTTP.HTTP_Status_Code.HTTP_Status_Code
import project.Network.HTTP.HTTP_Version.HTTP_Version
import project.Network.URI.URI
import project.Nothing.Nothing
import project.Panic.Panic
import project.Polyglot.Java
import project.Polyglot.Polyglot
import project.Random.Random
import project.Runtime
import project.System
import project.System.Environment
import project.System.File.Existing_File_Behavior.Existing_File_Behavior
import project.System.File.File
import project.System.File.File_Access.File_Access
import project.System.File.File_Permissions.File_Permissions
import project.System.File.File_Permissions.Permission
import project.System.File.Write_Extensions
import project.System.File_Format
import project.System.Platform
import project.System.Process
import project.System.Process.Exit_Code.Exit_Code
import project.Warning.Warning
from project.Data import Raw_Response
from project.Data.Index_Sub_Range.Index_Sub_Range import First, Last
from project.Data.Json.Extensions import all
from project.Data.Range.Extensions import all
from project.Data.Text.Extensions import all
from project.Data.Text.Regex import regex
from project.Errors.Problem_Behavior.Problem_Behavior import all
from project.Meta.Enso_Project import enso_project
from project.Network.Extensions import all
from project.System.File_Format.Plain_Text_Format import Plain_Text
export project.Any.Any
export project.Data
export project.Data.Array.Array

View File

@ -1,20 +1,3 @@
import project.Column_Description.Column_Description
import project.Connection.Client_Certificate.Client_Certificate
import project.Connection.Connection_Options.Connection_Options
import project.Connection.Credentials.Credentials
import project.Connection.Database
import project.Connection.Postgres_Details.Postgres_Details
import project.Connection.SQLite_Details.In_Memory
import project.Connection.SQLite_Details.SQLite_Details
import project.Connection.SQLite_Format.SQLite_Format
import project.Connection.SSL_Mode.SSL_Mode
import project.SQL_Query.SQL_Query
import project.Update_Action.Update_Action
from project.Connection.Postgres_Details.Postgres_Details import Postgres
from project.Connection.SQLite_Details.SQLite_Details import SQLite
from project.Extensions.Upload_Database_Table import all
from project.Extensions.Upload_In_Memory_Table import all
export project.Column_Description.Column_Description
export project.Connection.Client_Certificate.Client_Certificate
export project.Connection.Connection_Options.Connection_Options

View File

@ -2,8 +2,6 @@ from Standard.Base import all
from Standard.Table import Table
import project.Geo_Json
from project.Geo_Json export geo_json_to_table
## UNSTABLE

View File

@ -1,7 +1,3 @@
import project.Google_Analytics
import project.Google_Credential.Google_Credential
import project.Google_Sheets.Google_Sheets
export project.Google_Analytics
export project.Google_Credential.Google_Credential
export project.Google_Sheets.Google_Sheets

View File

@ -1,10 +1,5 @@
from Standard.Base import all
import project.Image.Image
import project.Matrix.Matrix
import project.Read_Flag.Read_Flag
import project.Write_Flag.Write_Flag
export project.Image.Image
export project.Matrix.Matrix
export project.Read_Flag.Read_Flag

View File

@ -1,2 +1 @@
import project.Snowflake_Details.Snowflake_Details
export project.Snowflake_Details.Snowflake_Details

View File

@ -1,34 +1,5 @@
from Standard.Base import all
import project.Aggregate_Column.Aggregate_Column
import project.Blank_Selector.Blank_Selector
import project.Column.Column
import project.Column_Operation.Column_Operation
import project.Column_Ref.Column_Ref
import project.Data_Formatter.Data_Formatter
import project.Delimited.Delimited_Format.Delimited_Format
import project.Delimited.Quote_Style.Quote_Style
import project.Excel.Excel_Format.Excel_Format
import project.Excel.Excel_Range.Excel_Range
import project.Excel.Excel_Workbook.Excel_Workbook
import project.Join_Condition.Join_Condition
import project.Join_Kind.Join_Kind
import project.Match_Columns.Match_Columns
import project.Position.Position
import project.Prefix_Name.Prefix_Name
import project.Set_Mode.Set_Mode
import project.Simple_Expression.Simple_Calculation
import project.Simple_Expression.Simple_Expression
import project.Sort_Column.Sort_Column
import project.Table.Table
import project.Value_Type.Auto
import project.Value_Type.Bits
import project.Value_Type.Value_Type
from project.Constants import all
from project.Expression import expr
from project.Extensions.Column_Vector_Extensions import all
from project.Extensions.Table_Conversions import all
export project.Aggregate_Column.Aggregate_Column
export project.Blank_Selector.Blank_Selector
export project.Column.Column

View File

@ -1,11 +1,5 @@
from Standard.Base import all
import project.AI
import project.File_Upload
import project.Helpers
import project.Id.Id
import project.Preprocessor
export project.AI
export project.Helpers

View File

@ -27,18 +27,28 @@ code from modules.
## Qualified Names
Both imports and exports require the use of qualified module names. A qualified
In the following text, **entity** shall denote a module, a method (instance,
static, foreign), type, or a type constructor. In other words, an _entity_ is
anything that can be assigned to a variable.
Both imports and exports require the use of qualified entity names. A qualified
name consists of the library namespace (usually organization under which its
published) and the library name, followed by module names mirroring the source
tree of the library. For example the file `src/Stuff/Things/Util.enso` inside
the library `My_Lib` published by the user `wdanilo` would have the following
qualified name: `wdanilo.My_Lib.Stuff.Things.Util`. To facilitate library
renaming (or deciding on the publishing organization later in the development
cycle, or working on a project that won't be published) it is possible to use
the keyword `project` instead of namespace and project name, to import a file in
the same project. Therefore, the file `src/Varia/Tools/Manager.enso` in `My_Lib`
published (or not) by `wdanilo` may use `project.Stuff.Things.Util` to refer to
the previously mentioned file.
tree of the library, followed by an entity name within that module. For example
the file `src/Stuff/Things/Util.enso` inside the library `My_Lib` published by
the user `wdanilo` would have the following qualified name:
`wdanilo.My_Lib.Stuff.Things.Util` and the type `My_Type` within that module
would have the qualified name `wdanilo.My_Lib.Stuff.Things.Util.My_Type`. To
facilitate library renaming (or deciding on the publishing organization later in
the development cycle, or working on a project that won't be published) it is
possible to use the keyword `project` instead of namespace and project name, to
import a file in the same project. Therefore, the file
`src/Varia/Tools/Manager.enso` in `My_Lib` published (or not) by `wdanilo` may
use `project.Stuff.Things.Util` to refer to the previously mentioned file.
Currently, the `project` keyword works only in import and export statements.
Note that it is possible to export a symbol from the current project with the
`project` keyword without first importing it.
## Import Syntax
@ -46,21 +56,21 @@ There are two main ways of importing a module into the current scope.
### Qualified Imports
These imports consist of the word `import` followed by a qualified name of a
module. This can by optionally followed by the `as` word, and a referent name of
the module as it should be visible in the importing scope.
These imports consist of the word `import` followed by a qualified name of an
entity. This can be optionally followed by the `as` word, and a referent name of
the entity as it should be visible in the importing scope.
The only name brought into scope by such an import is the name of the module (or
The only name brought into scope by such an import is the name of the entity (or
the name provided after the `as` keyword, if provided).
### Unqualified Imports
Unqualified imports are broken up into three main categories:
1. **Unrestricted Imports:** These import all symbols from the module into the
current scope. They consist of the keyword `from`, followed by a qualified
module name, followed by an optional rename part (using the `as` keyword),
then the keywords `import all`. For example:
1. **Unrestricted Imports:** These import all symbols (entities) from the module
(or from a type) into the current scope. They consist of the keyword `from`,
followed by a qualified module name, followed by an optional rename part
(using the `as` keyword), then the keywords `import all`. For example:
```
from Standard.Base.Data.List as Builtin_List import all
```
@ -81,8 +91,9 @@ Unqualified imports are broken up into three main categories:
from Standard.Base.Data.List import all hiding Cons, Nil
```
Imports in Enso _may_ introduce ambiguous symbols, but this is not an error
until one of the ambiguous symbols is _used_ in Enso code.
Imports in Enso _may_ introduce ambiguous symbols, which is treated as a
compilation error. Ideally, the error should be delayed until one of the
ambiguous symbols is _used_ in Enso code.
## Export Syntax
@ -92,44 +103,44 @@ appear in Enso as follows:
### Qualified Exports
These exports consist of the word `export` followed by a qualified name of a
module. This can by optionally followed by the `as` word, and a referent name of
the module as it should be visible in the exporting scope.
These exports consist of the word `export` followed by a qualified name of an
entity. This can be optionally followed by the `as` word, and a referent name of
the entity as it should be visible in the exporting scope.
The only name brought into scope by such an export is the name of the module (or
The only name brought into scope by such an export is the name of the entity (or
the name provided after the `as` keyword, if provided).
### Unqualified Exports
Unqualified exports are broken up into three main categories:
1. **Unrestricted Exports:** These export all symbols from the module into the
current scope. They consist of the keyword `from`, followed by a qualified
module name, followed by an optional rename part (using the `as` keyword),
then the keywords `export all`. For example:
1. **Unrestricted Exports:** These export all symbols from the module or type
into the current scope. They consist of the keyword `from`, followed by a
qualified module name, followed by an optional rename part (using the `as`
keyword), then the keywords `export all`. For example:
```
from Standard.Base.Data.List as Builtin_List export all
```
2. **Restricted Exports:** These export a specified set of names, behaving as
though they were redefined in the current scope. They consist of the keyword
`from`, followed by a qualified module name (with optional `as`-rename), then
the word `export` followed by a coma-separated list of names to be exported.
For example:
`from`, followed by a qualified module or type name (with optional
`as`-rename), then the word `export` followed by a coma-separated list of
names to be exported. For example:
```
from Standard.Base.Data.List export Cons, Nil, from_vector
```
3. **Hiding Exports:** These are the inverse of restricted exports, and export
_all_ symbols other than the named ones. They consist of the `from` keyword,
followed by a qualified module name (with optional `as`-rename), then the
words `export all hiding`, followed by a coma-separated list of names to be
excluded from the export. For example:
followed by a qualified module or type name (with optional `as`-rename), then
the words `export all hiding`, followed by a coma-separated list of names to
be excluded from the export. For example:
```
from Standard.Base.Data.List export all hiding from_vector, Nil
```
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 must be resolved at the _export_ site.
In essence, an export allows the user to "paste" the contents of the module or
type being exported into the module declaring the export. This means that
exports that create name clashes must be resolved at the _export_ site.
### Visibility of Export Bindings

View File

@ -19,7 +19,13 @@ public abstract class ImportResolverAlgorithm<
protected abstract String nameForType(ResolvedType e);
protected abstract java.util.List<Export> exportsFor(Module module, String impName);
/**
* Returns a list of all the exports from the module of the given symbol
*
* @param module the module to search for exports in
* @param symbol FQN symbol contained in the returned exports.
*/
protected abstract java.util.List<Export> exportsFor(Module module, String symbol);
protected abstract boolean isAll(Export ex);

View File

@ -3,10 +3,12 @@ package org.enso.compiler.phase
import org.enso.compiler.Compiler
import org.enso.compiler.context.CompilerContext.Module
import org.enso.compiler.core.Implicits.AsMetadata
import org.enso.compiler.core.ir.module.scope.Import
import org.enso.compiler.core.ir.{DiagnosticStorage, MetadataStorage}
import org.enso.compiler.core.ir.module.scope.{Export, Import}
import org.enso.compiler.data.BindingsMap
import org.enso.compiler.pass.analyse.BindingAnalysis
import org.enso.polyglot.CompilationStage
import scala.collection.mutable
import java.io.IOException
@ -72,8 +74,18 @@ final class ImportResolver(compiler: Compiler) extends ImportResolverForIR {
tryResolveImport(ir, imp)
case other => (other, None)
}
currentLocal.resolvedImports = importedModules.flatMap(_._2)
val newIr = ir.copy(imports = importedModules.map(_._1))
val resolvedImports = importedModules.flatMap(_._2)
val syntheticImports = addSyntheticImports(current, resolvedImports)
val resolvedSyntheticImports = syntheticImports.map(_._2)
val newImportIRs =
importedModules.map(_._1) ++ syntheticImports.map(_._1)
currentLocal.resolvedImports =
resolvedImports ++ resolvedSyntheticImports
val newIr = ir.copy(imports = newImportIRs)
context.updateModule(
current,
{ u =>
@ -151,4 +163,60 @@ final class ImportResolver(compiler: Compiler) extends ImportResolverForIR {
private[phase] def getCompiler(): Compiler = compiler
/** Traverses all the exports from the IR. Looks for exports that do not have associated imports.
* Note that it is valid to export an entity via fully qualified name without importing it first
* (at least in the current project).
* @param module IR of the module.
* @param resolvedImports List of all the resolved imports gathered so far from import IRs.
* @return List of tuples - first is the synthetic import IR, second is its resolved import.
*/
private def addSyntheticImports(
module: Module,
resolvedImports: List[BindingsMap.ResolvedImport]
): List[(Import, BindingsMap.ResolvedImport)] = {
val resolvedImportNames = resolvedImports.map(_.importDef.name.name)
val curModName = module.getName.toString
module.getIr.exports.flatMap {
case Export.Module(
expName,
rename,
isAll,
onlyNames,
hiddenNames,
_,
isSynthetic,
_,
_
) if !isSynthetic =>
val exportsItself = curModName.equals(expName.name)
// Skip the exports that already have associated resolved import.
if (!exportsItself && !resolvedImportNames.contains(expName.name)) {
val syntheticImport = Import.Module(
expName,
rename,
isAll,
onlyNames,
hiddenNames,
location = None,
isSynthetic = true,
passData = new MetadataStorage(),
diagnostics = DiagnosticStorage()
)
tryResolveImport(module.getIr, syntheticImport) match {
case (_, Some(resolvedImp)) =>
Some(
(
syntheticImport,
resolvedImp
)
)
case _ => None
}
} else {
None
}
case _ => None
}
}
}

View File

@ -1,3 +1 @@
import project.Sub.B
export project.Sub.B

View File

@ -1,3 +1 @@
import project.Sub.C
from project.Sub.C export all

View File

@ -1,3 +1 @@
import project.Sub.A
export project.Sub.A

View File

@ -12,8 +12,5 @@
import local.Test_Fully_Qualified_Name_1
main = Test_Fully_Qualified_Name_1.Synthetic_Mod.A_Mod.A_Type
import project.Synthetic_Mod.A_Mod.A_Type
from project.Synthetic_Mod.A_Mod import static_method
export project.Synthetic_Mod.A_Mod.A_Type
from project.Synthetic_Mod.A_Mod export static_method

View File

@ -1,5 +1,3 @@
import project.F1
from project.F1 import foo
export project.F1
from project.F1 export foo
from project.F1 export all hiding foo

View File

@ -1,6 +1,3 @@
import project.F1
from project.F1 import foo
export project.F1
from project.F1 export all
from project.F1 export all hiding bar

View File

@ -1,4 +1,2 @@
import project.F1
from project.F1 import foo
export project.F1
from project.F1 export foo

View File

@ -1,5 +1,4 @@
# Export some "public" stuff from this library
from project.Pub_Mod import Pub_Mod_Type
from project.Pub_Mod export Pub_Mod_Type
# Can import and use private modules in the same project

View File

@ -1,4 +1,3 @@
import project.Sub
export project.Sub
main =

View File

@ -1,4 +1,3 @@
import project.Sub.Pub
export project.Sub.Pub
main =

View File

@ -1,2 +1 @@
import project.Type_Def.Maybe
from project.Type_Def.Maybe export Some
from project.Type_Def.Maybe export Some

View File

@ -424,6 +424,84 @@ class ImportExportTest
.asInstanceOf[errors.ImportExport.SymbolDoesNotExist]
.symbolName shouldEqual "A_Type"
}
"export type without import should insert synthetic import" in {
s"""
|type A_Type
|""".stripMargin
.createModule(packageQualifiedName.createChild("A_Module"))
val mainModule = s"""
|export $namespace.$packageName.A_Module.A_Type
|""".stripMargin
.createModule(packageQualifiedName.createChild("Main"))
mainModule.getIr.imports.size shouldBe 1
mainModule.getIr.imports.head
.asInstanceOf[Import.Module]
.isSynthetic shouldBe true
val resolvedExports = mainModule.getIr.unwrapBindingMap.resolvedExports
resolvedExports.size shouldBe 1
resolvedExports.head.target.qualifiedName.item shouldBe "A_Type"
}
"(from) export type without import should insert synthetic import" in {
s"""
|type A_Type
|""".stripMargin
.createModule(packageQualifiedName.createChild("A_Module"))
val mainModule = s"""
|from $namespace.$packageName.A_Module export A_Type
|""".stripMargin
.createModule(packageQualifiedName.createChild("Main"))
mainModule.getIr.imports.size shouldBe 1
mainModule.getIr.imports.head
.asInstanceOf[Import.Module]
.isSynthetic shouldBe true
val resolvedExports = mainModule.getIr.unwrapBindingMap.resolvedExports
resolvedExports.size shouldBe 1
}
"export module without import should insert synthetic import" in {
// A_Module is empty on purpose
"""
|""".stripMargin
.createModule(packageQualifiedName.createChild("A_Module"))
val mainModule = s"""
|export $namespace.$packageName.A_Module
|""".stripMargin
.createModule(packageQualifiedName.createChild("Main"))
mainModule.getIr.imports.size shouldBe 1
mainModule.getIr.imports.head
.asInstanceOf[Import.Module]
.isSynthetic shouldBe true
val resolvedExports = mainModule.getIr.unwrapBindingMap.resolvedExports
resolvedExports.size shouldBe 1
resolvedExports.head.target.qualifiedName.item shouldBe "A_Module"
}
"export unknown type without import should result in error" in {
s"""
|type A_Type
|""".stripMargin
.createModule(packageQualifiedName.createChild("A_Module"))
val mainModule = s"""
|from $namespace.$packageName.A_Module export UNKNOWN_TYPE_FOO
|""".stripMargin
.createModule(packageQualifiedName.createChild("Main"))
mainModule.getIr.imports.size shouldBe 1
mainModule.getIr.imports.head
.isInstanceOf[errors.ImportExport] shouldBe true
}
}
"Exporting non-existing symbols" should {

View File

@ -1,4 +1,3 @@
from project.Data.Boolean.Boolean import all
from project.Data.Boolean.Boolean export all
@Builtin_Type

View File

@ -1,44 +1,15 @@
import project.IO
export project.IO
import project.Panic.Panic
export project.Panic.Panic
import project.Error.Error
export project.Error.Error
import project.Any.Any
export project.Any.Any
import project.Data.Array.Array
export project.Data.Array.Array
import project.Data.Boolean
from project.Data.Boolean export Boolean, True, False
import project.Data.Text.Text
export project.Data.Text.Text
import project.Data.Time.Date.Date
export project.Data.Time.Date.Date
import project.Data.List.List
export project.Data.List.List
import project.Data.Numbers
from project.Data.Numbers export Number, Integer
import project.Data.Vector.Vector
export project.Data.Vector.Vector
import project.Polyglot.Java
export project.Polyglot.Java
import project.Polyglot.Polyglot
export project.Polyglot.Polyglot
import project.Runtime
export project.Runtime
import project.Warning
export project.Warning

View File

@ -1,2 +1 @@
import project.Api
export project.Api