diff --git a/toolchain/check/check.cpp b/toolchain/check/check.cpp index 1fb42f89e5ae..66f35fd9a0ca 100644 --- a/toolchain/check/check.cpp +++ b/toolchain/check/check.cpp @@ -200,6 +200,82 @@ struct UnitInfo { UnitInfo* api_for_impl = nullptr; }; +// Imports the current package. +static auto ImportCurrentPackage(Context& context, UnitInfo& unit_info, + int total_ir_count, + SemIR::InstId package_inst_id, + SemIR::TypeId namespace_type_id) -> void { + // Add imports from the current package. + auto self_import = unit_info.package_imports_map.find(IdentifierId::Invalid); + if (self_import == unit_info.package_imports_map.end()) { + // Push the scope; there are no names to add. + context.scope_stack().Push(package_inst_id, SemIR::NameScopeId::Package); + return; + } + + // Track whether an IR was imported in full, including `export import`. This + // distinguishes from IRs that are indirectly added without all names being + // exported to this IR. + llvm::SmallVector imported_irs(total_ir_count, false); + if (self_import->second.has_load_error) { + context.name_scopes().Get(SemIR::NameScopeId::Package).has_error = true; + } + + for (const auto& import : self_import->second.imports) { + const auto& import_sem_ir = **import.unit_info->unit->sem_ir; + + auto& imported_ir = imported_irs[import_sem_ir.check_ir_id().index]; + if (!imported_ir) { + imported_ir = true; + + // Import the IR and its exported imports. + ImportLibraryFromCurrentPackage(context, namespace_type_id, + import.names.node_id, import_sem_ir, + import.names.is_export); + + for (const auto& indirect_ir : import_sem_ir.import_irs().array_ref()) { + if (indirect_ir.is_export) { + auto& imported_indirect_ir = + imported_irs[indirect_ir.sem_ir->check_ir_id().index]; + if (!imported_indirect_ir) { + imported_indirect_ir = true; + + ImportLibraryFromCurrentPackage( + context, namespace_type_id, import.names.node_id, + *indirect_ir.sem_ir, import.names.is_export); + } else if (import.names.is_export) { + // The indirect IR was previously indirectly imported, but it's + // found through `export import`. We need to mark it for re-export. + context.import_irs() + .Get(context.check_ir_map()[indirect_ir.sem_ir->check_ir_id() + .index]) + .is_export = true; + } + } + } + } else if (import.names.is_export) { + // The IR was previously indirectly imported, but it's `export import`. + // We need to mark it -- and transitive `export import`s -- for re-export. + context.import_irs() + .Get(context.check_ir_map()[import_sem_ir.check_ir_id().index]) + .is_export = true; + + for (const auto& indirect_ir : import_sem_ir.import_irs().array_ref()) { + if (indirect_ir.is_export) { + context.import_irs() + .Get(context + .check_ir_map()[indirect_ir.sem_ir->check_ir_id().index]) + .is_export = true; + } + } + } + } + + context.scope_stack().Push( + package_inst_id, SemIR::NameScopeId::Package, + context.name_scopes().Get(SemIR::NameScopeId::Package).has_error); +} + // Add imports to the root block. static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info, int total_ir_count) -> void { @@ -248,30 +324,8 @@ static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info, {.node_id = Parse::InvalidNodeId(), .sem_ir = nullptr}); } - // Add imports from the current package. - auto self_import = unit_info.package_imports_map.find(IdentifierId::Invalid); - if (self_import != unit_info.package_imports_map.end()) { - bool error_in_import = self_import->second.has_load_error; - for (const auto& import : self_import->second.imports) { - const auto& import_sem_ir = **import.unit_info->unit->sem_ir; - ImportLibraryFromCurrentPackage(context, namespace_type_id, - import.names.node_id, import_sem_ir); - error_in_import |= import_sem_ir.name_scopes() - .Get(SemIR::NameScopeId::Package) - .has_error; - } - - // If an import of the current package caused an error for the imported - // file, it transitively affects the current file too. - if (error_in_import) { - context.name_scopes().Get(SemIR::NameScopeId::Package).has_error = true; - } - context.scope_stack().Push(package_inst_id, SemIR::NameScopeId::Package, - error_in_import); - } else { - // Push the scope; there are no names to add. - context.scope_stack().Push(package_inst_id, SemIR::NameScopeId::Package); - } + ImportCurrentPackage(context, unit_info, total_ir_count, package_inst_id, + namespace_type_id); CARBON_CHECK(context.scope_stack().PeekIndex() == ScopeIndex::Package); for (auto& [package_id, package_imports] : unit_info.package_imports_map) { @@ -283,7 +337,10 @@ static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info, llvm::SmallVector import_irs; for (auto import : package_imports.imports) { import_irs.push_back({.node_id = import.names.node_id, - .sem_ir = &**import.unit_info->unit->sem_ir}); + .sem_ir = &**import.unit_info->unit->sem_ir, + .is_export = false}); + CARBON_CHECK(!import.names.is_export) + << "Imports from other packages can't be exported."; } ImportLibrariesFromOtherPackage(context, namespace_type_id, package_imports.node_id, package_id, diff --git a/toolchain/check/handle_export.cpp b/toolchain/check/handle_export.cpp new file mode 100644 index 000000000000..ab78fbf2a9da --- /dev/null +++ b/toolchain/check/handle_export.cpp @@ -0,0 +1,20 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "toolchain/check/context.h" +#include "toolchain/parse/typed_nodes.h" + +namespace Carbon::Check { + +auto HandleExportIntroducer(Context& /*context*/, + Parse::ExportIntroducerId /*node_id*/) -> bool { + return true; +} + +auto HandleExportDirective(Context& context, Parse::ExportDirectiveId node_id) + -> bool { + return context.TODO(node_id, "ExportDirective"); +} + +} // namespace Carbon::Check diff --git a/toolchain/check/handle_import_and_package.cpp b/toolchain/check/handle_import_and_package.cpp index 822e240a0395..327ed215d873 100644 --- a/toolchain/check/handle_import_and_package.cpp +++ b/toolchain/check/handle_import_and_package.cpp @@ -14,6 +14,11 @@ auto HandleImportIntroducer(Context& /*context*/, return true; } +auto HandleImportExport(Context& /*context*/, Parse::ImportExportId /*node_id*/) + -> bool { + return true; +} + auto HandleImportDirective(Context& /*context*/, Parse::ImportDirectiveId /*node_id*/) -> bool { return true; diff --git a/toolchain/check/import.cpp b/toolchain/check/import.cpp index 753b84ba9caf..4268a7f59750 100644 --- a/toolchain/check/import.cpp +++ b/toolchain/check/import.cpp @@ -213,9 +213,11 @@ static auto CopyEnclosingNameScopesFromImportIR( auto ImportLibraryFromCurrentPackage(Context& context, SemIR::TypeId namespace_type_id, Parse::ImportDirectiveId node_id, - const SemIR::File& import_sem_ir) -> void { - auto ir_id = - AddImportIR(context, {.node_id = node_id, .sem_ir = &import_sem_ir}); + const SemIR::File& import_sem_ir, + bool is_export) -> void { + auto ir_id = AddImportIR( + context, + {.node_id = node_id, .sem_ir = &import_sem_ir, .is_export = is_export}); context.import_ir_constant_values()[ir_id.index].Set( SemIR::InstId::PackageNamespace, @@ -257,6 +259,12 @@ auto ImportLibraryFromCurrentPackage(Context& context, } } } + + // If an import of the current package caused an error for the imported + // file, it transitively affects the current file too. + if (import_sem_ir.name_scopes().Get(SemIR::NameScopeId::Package).has_error) { + context.name_scopes().Get(SemIR::NameScopeId::Package).has_error = true; + } } auto ImportLibrariesFromOtherPackage(Context& context, diff --git a/toolchain/check/import.h b/toolchain/check/import.h index e680a3f31545..bb40ac02d80a 100644 --- a/toolchain/check/import.h +++ b/toolchain/check/import.h @@ -17,7 +17,8 @@ namespace Carbon::Check { auto ImportLibraryFromCurrentPackage(Context& context, SemIR::TypeId namespace_type_id, Parse::ImportDirectiveId node_id, - const SemIR::File& import_sem_ir) -> void; + const SemIR::File& import_sem_ir, + bool is_export) -> void; // Adds another package's imports to name lookup, with all libraries together. // This only adds the package name to lookup, so that `package.ImportedPackage` diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index 9a1ef21411e7..618b06624a18 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -44,6 +44,10 @@ auto AddImportIR(Context& context, SemIR::ImportIR import_ir) if (!ir_id.is_valid()) { // Note this updates check_ir_map. ir_id = InternalAddImportIR(context, import_ir); + } else if (import_ir.is_export) { + // We're processing an `export import`. In case the IR was indirectly added + // as a non-export, mark it as an export. + context.import_irs().Get(ir_id).is_export = true; } return ir_id; } @@ -246,8 +250,9 @@ class ImportRefResolver { cursor_ir_id = context_.check_ir_map()[cursor_ir->check_ir_id().index]; if (!cursor_ir_id.is_valid()) { // TODO: Should we figure out a location to assign here? - cursor_ir_id = AddImportIR( - context_, {.node_id = Parse::NodeId::Invalid, .sem_ir = cursor_ir}); + cursor_ir_id = AddImportIR(context_, {.node_id = Parse::NodeId::Invalid, + .sem_ir = cursor_ir, + .is_export = false}); } cursor_inst_id = ir_inst.inst_id; diff --git a/toolchain/check/testdata/packages/no_prelude/export_import.carbon b/toolchain/check/testdata/packages/no_prelude/export_import.carbon new file mode 100644 index 000000000000..45d6aaa0240c --- /dev/null +++ b/toolchain/check/testdata/packages/no_prelude/export_import.carbon @@ -0,0 +1,726 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// ============================================================================ +// Setup files +// ============================================================================ + +// --- base.carbon + +library "base" api; + +class C { + var x: (); +}; + +// --- export.carbon + +library "export" api; + +export import library "base"; + +// --- export_copy.carbon + +library "export_copy" api; + +export import library "base"; + +// --- non_export.carbon + +library "non_export" api; + +import library "export"; + +// --- export_export.carbon + +library "export_export" api; + +export import library "export"; + +// --- import_then_export.carbon + +library "import_then_export" api; + +import library "base"; +export import library "export"; + +// --- export_in_impl.carbon + +// This is just providing an API for the implicit import. +library "export_in_impl" api; + +// ============================================================================ +// Test files +// ============================================================================ + +// --- use_export.carbon + +library "use_export" api; + +import library "export"; + +var c: C = {.x = ()}; + +// --- fail_export_in_impl.impl.carbon + +library "export_in_impl" impl; + +// CHECK:STDERR: fail_export_in_impl.impl.carbon:[[@LINE+4]]:1: ERROR: `export` is only allowed in API files. +// CHECK:STDERR: export import library "base"; +// CHECK:STDERR: ^~~~~~ +// CHECK:STDERR: +export import library "base"; + +// Note the import still occurs. +var c: C = {.x = ()}; + +// --- export_export.impl.carbon + +library "export_export" impl; + +var c: C = {.x = ()}; + +// --- use_export_export.carbon + +library "use_export_export" api; + +import library "export_export"; + +var c: C = {.x = ()}; + +// --- use_import_then_export.carbon + +library "use_import_then_export" api; + +import library "import_then_export"; + +var c: C = {.x = ()}; + +// --- use_import_when_export.carbon + +library "use_import_when_export" api; + +export import library "base"; + +var c: C = {.x = ()}; + +// --- use_base_and_export.carbon + +library "use_base_and_export" api; + +import library "base"; +import library "export"; + +var c: C = {.x = ()}; + +// --- use_export_and_base.carbon + +library "import_both_reversed" api; + +import library "export"; +import library "base"; + +var c: C = {.x = ()}; + +// --- use_export_copy.carbon + +library "use_export_copy" api; + +import library "export"; +import library "export_copy"; + +var c: C = {.x = ()}; + +// --- fail_use_non_export.carbon + +library "fail_use_non_export" api; + +import library "non_export"; + +// CHECK:STDERR: fail_use_non_export.carbon:[[@LINE+3]]:15: ERROR: Name `C` not found. +// CHECK:STDERR: alias Local = C; +// CHECK:STDERR: ^ +alias Local = C; + +// --- use_non_export_then_base.carbon + +library "use_non_export_then_base" api; + +import library "non_export"; +export import library "base"; + +var c: C = {.x = ()}; + +// --- indirect_use_non_export_then_base.carbon + +library "indirect_use_non_export_then_base" api; + +import library "use_non_export_then_base"; + +var indirect_c: C = {.x = ()}; + +// CHECK:STDOUT: --- base.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = unbound_element_type C, () [template] +// CHECK:STDOUT: %.3: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %.loc5_11.1: () = tuple_literal () +// CHECK:STDOUT: %.loc5_11.2: type = converted %.loc5_11.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: %.loc5_8: = field_decl x, element0 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .x = %.loc5_8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- export.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref = import_ref ir1, inst+1, unloaded +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- export_copy.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref = import_ref ir1, inst+1, unloaded +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- non_export.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref = import_ref ir2, inst+1, unloaded +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- export_export.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref = import_ref ir2, inst+1, unloaded +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- import_then_export.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref = import_ref ir1, inst+1, unloaded +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- export_in_impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_export.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: ()} [template] +// CHECK:STDOUT: %tuple: () = tuple_value () [template] +// CHECK:STDOUT: %struct: C = struct_value (%tuple) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.2 = import_ref ir2, inst+7, unloaded +// CHECK:STDOUT: %import_ref.3 = import_ref ir2, inst+2, unloaded +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C] +// CHECK:STDOUT: %c.var: ref C = var c +// CHECK:STDOUT: %c: ref C = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = file.%import_ref.2 +// CHECK:STDOUT: .Self = file.%import_ref.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc6_19.1: () = tuple_literal () +// CHECK:STDOUT: %.loc6_20.1: {.x: ()} = struct_literal (%.loc6_19.1) +// CHECK:STDOUT: %.loc6_20.2: ref () = class_element_access file.%c.var, element0 +// CHECK:STDOUT: %.loc6_19.2: init () = tuple_init () to %.loc6_20.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc6_20.3: init () = converted %.loc6_19.1, %.loc6_19.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc6_20.4: init C = class_init (%.loc6_20.3), file.%c.var [template = constants.%struct] +// CHECK:STDOUT: %.loc6_21: init C = converted %.loc6_20.1, %.loc6_20.4 [template = constants.%struct] +// CHECK:STDOUT: assign file.%c.var, %.loc6_21 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_export_in_impl.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: ()} [template] +// CHECK:STDOUT: %tuple: () = tuple_value () [template] +// CHECK:STDOUT: %struct: C = struct_value (%tuple) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir1, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.2 = import_ref ir1, inst+7, unloaded +// CHECK:STDOUT: %import_ref.3 = import_ref ir1, inst+2, unloaded +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C] +// CHECK:STDOUT: %c.var: ref C = var c +// CHECK:STDOUT: %c: ref C = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = file.%import_ref.2 +// CHECK:STDOUT: .Self = file.%import_ref.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc11_19.1: () = tuple_literal () +// CHECK:STDOUT: %.loc11_20.1: {.x: ()} = struct_literal (%.loc11_19.1) +// CHECK:STDOUT: %.loc11_20.2: ref () = class_element_access file.%c.var, element0 +// CHECK:STDOUT: %.loc11_19.2: init () = tuple_init () to %.loc11_20.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc11_20.3: init () = converted %.loc11_19.1, %.loc11_19.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc11_20.4: init C = class_init (%.loc11_20.3), file.%c.var [template = constants.%struct] +// CHECK:STDOUT: %.loc11_21: init C = converted %.loc11_20.1, %.loc11_20.4 [template = constants.%struct] +// CHECK:STDOUT: assign file.%c.var, %.loc11_21 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- export_export.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: ()} [template] +// CHECK:STDOUT: %tuple: () = tuple_value () [template] +// CHECK:STDOUT: %struct: C = struct_value (%tuple) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.2 = import_ref ir2, inst+7, unloaded +// CHECK:STDOUT: %import_ref.3 = import_ref ir2, inst+2, unloaded +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C] +// CHECK:STDOUT: %c.var: ref C = var c +// CHECK:STDOUT: %c: ref C = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = file.%import_ref.2 +// CHECK:STDOUT: .Self = file.%import_ref.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc4_19.1: () = tuple_literal () +// CHECK:STDOUT: %.loc4_20.1: {.x: ()} = struct_literal (%.loc4_19.1) +// CHECK:STDOUT: %.loc4_20.2: ref () = class_element_access file.%c.var, element0 +// CHECK:STDOUT: %.loc4_19.2: init () = tuple_init () to %.loc4_20.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc4_20.3: init () = converted %.loc4_19.1, %.loc4_19.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc4_20.4: init C = class_init (%.loc4_20.3), file.%c.var [template = constants.%struct] +// CHECK:STDOUT: %.loc4_21: init C = converted %.loc4_20.1, %.loc4_20.4 [template = constants.%struct] +// CHECK:STDOUT: assign file.%c.var, %.loc4_21 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_export_export.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: ()} [template] +// CHECK:STDOUT: %tuple: () = tuple_value () [template] +// CHECK:STDOUT: %struct: C = struct_value (%tuple) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir3, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.2 = import_ref ir3, inst+7, unloaded +// CHECK:STDOUT: %import_ref.3 = import_ref ir3, inst+2, unloaded +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C] +// CHECK:STDOUT: %c.var: ref C = var c +// CHECK:STDOUT: %c: ref C = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = file.%import_ref.2 +// CHECK:STDOUT: .Self = file.%import_ref.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc6_19.1: () = tuple_literal () +// CHECK:STDOUT: %.loc6_20.1: {.x: ()} = struct_literal (%.loc6_19.1) +// CHECK:STDOUT: %.loc6_20.2: ref () = class_element_access file.%c.var, element0 +// CHECK:STDOUT: %.loc6_19.2: init () = tuple_init () to %.loc6_20.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc6_20.3: init () = converted %.loc6_19.1, %.loc6_19.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc6_20.4: init C = class_init (%.loc6_20.3), file.%c.var [template = constants.%struct] +// CHECK:STDOUT: %.loc6_21: init C = converted %.loc6_20.1, %.loc6_20.4 [template = constants.%struct] +// CHECK:STDOUT: assign file.%c.var, %.loc6_21 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_import_then_export.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: ()} [template] +// CHECK:STDOUT: %tuple: () = tuple_value () [template] +// CHECK:STDOUT: %struct: C = struct_value (%tuple) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.2 = import_ref ir2, inst+7, unloaded +// CHECK:STDOUT: %import_ref.3 = import_ref ir2, inst+2, unloaded +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C] +// CHECK:STDOUT: %c.var: ref C = var c +// CHECK:STDOUT: %c: ref C = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = file.%import_ref.2 +// CHECK:STDOUT: .Self = file.%import_ref.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc6_19.1: () = tuple_literal () +// CHECK:STDOUT: %.loc6_20.1: {.x: ()} = struct_literal (%.loc6_19.1) +// CHECK:STDOUT: %.loc6_20.2: ref () = class_element_access file.%c.var, element0 +// CHECK:STDOUT: %.loc6_19.2: init () = tuple_init () to %.loc6_20.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc6_20.3: init () = converted %.loc6_19.1, %.loc6_19.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc6_20.4: init C = class_init (%.loc6_20.3), file.%c.var [template = constants.%struct] +// CHECK:STDOUT: %.loc6_21: init C = converted %.loc6_20.1, %.loc6_20.4 [template = constants.%struct] +// CHECK:STDOUT: assign file.%c.var, %.loc6_21 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_import_when_export.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: ()} [template] +// CHECK:STDOUT: %tuple: () = tuple_value () [template] +// CHECK:STDOUT: %struct: C = struct_value (%tuple) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir1, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.2 = import_ref ir1, inst+7, unloaded +// CHECK:STDOUT: %import_ref.3 = import_ref ir1, inst+2, unloaded +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C] +// CHECK:STDOUT: %c.var: ref C = var c +// CHECK:STDOUT: %c: ref C = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = file.%import_ref.2 +// CHECK:STDOUT: .Self = file.%import_ref.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc6_19.1: () = tuple_literal () +// CHECK:STDOUT: %.loc6_20.1: {.x: ()} = struct_literal (%.loc6_19.1) +// CHECK:STDOUT: %.loc6_20.2: ref () = class_element_access file.%c.var, element0 +// CHECK:STDOUT: %.loc6_19.2: init () = tuple_init () to %.loc6_20.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc6_20.3: init () = converted %.loc6_19.1, %.loc6_19.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc6_20.4: init C = class_init (%.loc6_20.3), file.%c.var [template = constants.%struct] +// CHECK:STDOUT: %.loc6_21: init C = converted %.loc6_20.1, %.loc6_20.4 [template = constants.%struct] +// CHECK:STDOUT: assign file.%c.var, %.loc6_21 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_base_and_export.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: ()} [template] +// CHECK:STDOUT: %tuple: () = tuple_value () [template] +// CHECK:STDOUT: %struct: C = struct_value (%tuple) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir1, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.2 = import_ref ir1, inst+7, unloaded +// CHECK:STDOUT: %import_ref.3 = import_ref ir1, inst+2, unloaded +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C] +// CHECK:STDOUT: %c.var: ref C = var c +// CHECK:STDOUT: %c: ref C = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = file.%import_ref.2 +// CHECK:STDOUT: .Self = file.%import_ref.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc7_19.1: () = tuple_literal () +// CHECK:STDOUT: %.loc7_20.1: {.x: ()} = struct_literal (%.loc7_19.1) +// CHECK:STDOUT: %.loc7_20.2: ref () = class_element_access file.%c.var, element0 +// CHECK:STDOUT: %.loc7_19.2: init () = tuple_init () to %.loc7_20.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc7_20.3: init () = converted %.loc7_19.1, %.loc7_19.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc7_20.4: init C = class_init (%.loc7_20.3), file.%c.var [template = constants.%struct] +// CHECK:STDOUT: %.loc7_21: init C = converted %.loc7_20.1, %.loc7_20.4 [template = constants.%struct] +// CHECK:STDOUT: assign file.%c.var, %.loc7_21 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_export_and_base.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: ()} [template] +// CHECK:STDOUT: %tuple: () = tuple_value () [template] +// CHECK:STDOUT: %struct: C = struct_value (%tuple) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.2 = import_ref ir2, inst+7, unloaded +// CHECK:STDOUT: %import_ref.3 = import_ref ir2, inst+2, unloaded +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C] +// CHECK:STDOUT: %c.var: ref C = var c +// CHECK:STDOUT: %c: ref C = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = file.%import_ref.2 +// CHECK:STDOUT: .Self = file.%import_ref.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc7_19.1: () = tuple_literal () +// CHECK:STDOUT: %.loc7_20.1: {.x: ()} = struct_literal (%.loc7_19.1) +// CHECK:STDOUT: %.loc7_20.2: ref () = class_element_access file.%c.var, element0 +// CHECK:STDOUT: %.loc7_19.2: init () = tuple_init () to %.loc7_20.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc7_20.3: init () = converted %.loc7_19.1, %.loc7_19.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc7_20.4: init C = class_init (%.loc7_20.3), file.%c.var [template = constants.%struct] +// CHECK:STDOUT: %.loc7_21: init C = converted %.loc7_20.1, %.loc7_20.4 [template = constants.%struct] +// CHECK:STDOUT: assign file.%c.var, %.loc7_21 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_export_copy.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: ()} [template] +// CHECK:STDOUT: %tuple: () = tuple_value () [template] +// CHECK:STDOUT: %struct: C = struct_value (%tuple) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.2 = import_ref ir2, inst+7, unloaded +// CHECK:STDOUT: %import_ref.3 = import_ref ir2, inst+2, unloaded +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C] +// CHECK:STDOUT: %c.var: ref C = var c +// CHECK:STDOUT: %c: ref C = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = file.%import_ref.2 +// CHECK:STDOUT: .Self = file.%import_ref.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc7_19.1: () = tuple_literal () +// CHECK:STDOUT: %.loc7_20.1: {.x: ()} = struct_literal (%.loc7_19.1) +// CHECK:STDOUT: %.loc7_20.2: ref () = class_element_access file.%c.var, element0 +// CHECK:STDOUT: %.loc7_19.2: init () = tuple_init () to %.loc7_20.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc7_20.3: init () = converted %.loc7_19.1, %.loc7_19.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc7_20.4: init C = class_init (%.loc7_20.3), file.%c.var [template = constants.%struct] +// CHECK:STDOUT: %.loc7_21: init C = converted %.loc7_20.1, %.loc7_20.4 [template = constants.%struct] +// CHECK:STDOUT: assign file.%c.var, %.loc7_21 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_use_non_export.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Local = %Local +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.ref: = name_ref C, [template = ] +// CHECK:STDOUT: %Local: = bind_alias Local, [template = ] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- use_non_export_then_base.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: ()} [template] +// CHECK:STDOUT: %tuple: () = tuple_value () [template] +// CHECK:STDOUT: %struct: C = struct_value (%tuple) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.2 = import_ref ir2, inst+7, unloaded +// CHECK:STDOUT: %import_ref.3 = import_ref ir2, inst+2, unloaded +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C] +// CHECK:STDOUT: %c.var: ref C = var c +// CHECK:STDOUT: %c: ref C = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = file.%import_ref.2 +// CHECK:STDOUT: .Self = file.%import_ref.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc7_19.1: () = tuple_literal () +// CHECK:STDOUT: %.loc7_20.1: {.x: ()} = struct_literal (%.loc7_19.1) +// CHECK:STDOUT: %.loc7_20.2: ref () = class_element_access file.%c.var, element0 +// CHECK:STDOUT: %.loc7_19.2: init () = tuple_init () to %.loc7_20.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc7_20.3: init () = converted %.loc7_19.1, %.loc7_19.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc7_20.4: init C = class_init (%.loc7_20.3), file.%c.var [template = constants.%struct] +// CHECK:STDOUT: %.loc7_21: init C = converted %.loc7_20.1, %.loc7_20.4 [template = constants.%struct] +// CHECK:STDOUT: assign file.%c.var, %.loc7_21 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- indirect_use_non_export_then_base.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: ()} [template] +// CHECK:STDOUT: %tuple: () = tuple_value () [template] +// CHECK:STDOUT: %struct: C = struct_value (%tuple) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .c = %import_ref.1 +// CHECK:STDOUT: .C = %import_ref.2 +// CHECK:STDOUT: .indirect_c = %indirect_c +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1 = import_ref ir1, inst+13, unloaded +// CHECK:STDOUT: %import_ref.2: type = import_ref ir2, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.3 = import_ref ir2, inst+7, unloaded +// CHECK:STDOUT: %import_ref.4 = import_ref ir2, inst+2, unloaded +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.2 [template = constants.%C] +// CHECK:STDOUT: %indirect_c.var: ref C = var indirect_c +// CHECK:STDOUT: %indirect_c: ref C = bind_name indirect_c, %indirect_c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = file.%import_ref.3 +// CHECK:STDOUT: .Self = file.%import_ref.4 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc6_28.1: () = tuple_literal () +// CHECK:STDOUT: %.loc6_29.1: {.x: ()} = struct_literal (%.loc6_28.1) +// CHECK:STDOUT: %.loc6_29.2: ref () = class_element_access file.%indirect_c.var, element0 +// CHECK:STDOUT: %.loc6_28.2: init () = tuple_init () to %.loc6_29.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc6_29.3: init () = converted %.loc6_28.1, %.loc6_28.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc6_29.4: init C = class_init (%.loc6_29.3), file.%indirect_c.var [template = constants.%struct] +// CHECK:STDOUT: %.loc6_30: init C = converted %.loc6_29.1, %.loc6_29.4 [template = constants.%struct] +// CHECK:STDOUT: assign file.%indirect_c.var, %.loc6_30 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/packages/no_prelude/export_name.carbon b/toolchain/check/testdata/packages/no_prelude/export_name.carbon new file mode 100644 index 000000000000..a8ebd267269f --- /dev/null +++ b/toolchain/check/testdata/packages/no_prelude/export_name.carbon @@ -0,0 +1,385 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// ============================================================================ +// Setup files +// ============================================================================ + +// --- class.carbon + +library "class" api; + +class C { + var x: (); +}; + +namespace NS; +class NS.NSC { + var x: (); +}; + +// --- fail_todo_export_class.carbon + +library "export_class" api; + +import library "class"; +// CHECK:STDERR: fail_todo_export_class.carbon:[[@LINE+4]]:1: ERROR: Semantics TODO: `ExportDirective`. +// CHECK:STDERR: export C; +// CHECK:STDERR: ^~~~~~~~~ +// CHECK:STDERR: +export C; + +// --- non_export_class.carbon + +library "non_export_class" api; + +import library "export_class"; + +// --- fail_todo_export_export_class.carbon + +library "export_export_class" api; + +import library "export_class"; +// CHECK:STDERR: fail_todo_export_export_class.carbon:[[@LINE+4]]:1: ERROR: Semantics TODO: `ExportDirective`. +// CHECK:STDERR: export C; +// CHECK:STDERR: ^~~~~~~~~ +// CHECK:STDERR: +export C; + +// --- export_in_impl.carbon + +// This is just providing an API for the implicit import. +library "export_in_impl" api; + +// ============================================================================ +// Test files +// ============================================================================ + +// --- fail_todo_use_export_class.carbon + +library "use_export_class" api; + +import library "export_class"; + +// CHECK:STDERR: fail_todo_use_export_class.carbon:[[@LINE+4]]:8: ERROR: Name `C` not found. +// CHECK:STDERR: var c: C = {.x = ()}; +// CHECK:STDERR: ^ +// CHECK:STDERR: +var c: C = {.x = ()}; + +// --- fail_todo_export_in_impl.impl.carbon + +library "export_in_impl" impl; + +import library "class"; +// CHECK:STDERR: fail_todo_export_in_impl.impl.carbon:[[@LINE+8]]:1: ERROR: `export` is only allowed in API files. +// CHECK:STDERR: export C; +// CHECK:STDERR: ^~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_todo_export_in_impl.impl.carbon:[[@LINE+4]]:1: ERROR: Semantics TODO: `ExportDirective`. +// CHECK:STDERR: export C; +// CHECK:STDERR: ^~~~~~~~~ +// CHECK:STDERR: +export C; + +// --- fail_todo_export_export_class.impl.carbon + +library "export_export_class" impl; + +// CHECK:STDERR: fail_todo_export_export_class.impl.carbon:[[@LINE+4]]:8: ERROR: Name `C` not found. +// CHECK:STDERR: var c: C = {.x = ()}; +// CHECK:STDERR: ^ +// CHECK:STDERR: +var c: C = {.x = ()}; + +// --- fail_todo_use_export_export_class.carbon + +library "use_export_export_class" api; + +import library "export_export_class"; + +// CHECK:STDERR: fail_todo_use_export_export_class.carbon:[[@LINE+4]]:8: ERROR: Name `C` not found. +// CHECK:STDERR: var c: C = {.x = ()}; +// CHECK:STDERR: ^ +// CHECK:STDERR: +var c: C = {.x = ()}; + +// --- import_both.carbon + +library "import_both" api; + +import library "class"; +import library "export_class"; + +var c: C = {.x = ()}; + +// --- import_both_reversed.carbon + +library "import_both_reversed" api; + +import library "export_class"; +import library "class"; + +var c: C = {.x = ()}; + +// --- fail_use_non_export_class.carbon + +library "fail_use_non_export_class" api; + +import library "non_export_class"; + +// CHECK:STDERR: fail_use_non_export_class.carbon:[[@LINE+3]]:15: ERROR: Name `C` not found. +// CHECK:STDERR: alias Local = C; +// CHECK:STDERR: ^ +alias Local = C; + +// CHECK:STDOUT: --- class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = unbound_element_type C, () [template] +// CHECK:STDOUT: %.3: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %NSC: type = class_type @NSC [template] +// CHECK:STDOUT: %.4: type = unbound_element_type NSC, () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .NS = %NS +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %NS: = namespace [template] { +// CHECK:STDOUT: .NSC = %NSC.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %NSC.decl: type = class_decl @NSC [template = constants.%NSC] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %.loc5_11.1: () = tuple_literal () +// CHECK:STDOUT: %.loc5_11.2: type = converted %.loc5_11.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: %.loc5_8: = field_decl x, element0 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .x = %.loc5_8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @NSC { +// CHECK:STDOUT: %.loc10_11.1: () = tuple_literal () +// CHECK:STDOUT: %.loc10_11.2: type = converted %.loc10_11.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: %.loc10_8: = field_decl x, element0 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%NSC +// CHECK:STDOUT: .x = %.loc10_8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_export_class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- non_export_class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_export_export_class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- export_in_impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_use_export_class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.ref: = name_ref C, [template = ] +// CHECK:STDOUT: %c.var: ref = var c +// CHECK:STDOUT: %c: ref = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc10_19: () = tuple_literal () +// CHECK:STDOUT: %.loc10_20: {.x: ()} = struct_literal (%.loc10_19) +// CHECK:STDOUT: assign file.%c.var, +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_export_in_impl.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_export_export_class.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.ref: = name_ref C, [template = ] +// CHECK:STDOUT: %c.var: ref = var c +// CHECK:STDOUT: %c: ref = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc8_19: () = tuple_literal () +// CHECK:STDOUT: %.loc8_20: {.x: ()} = struct_literal (%.loc8_19) +// CHECK:STDOUT: assign file.%c.var, +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_use_export_export_class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.ref: = name_ref C, [template = ] +// CHECK:STDOUT: %c.var: ref = var c +// CHECK:STDOUT: %c: ref = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc10_19: () = tuple_literal () +// CHECK:STDOUT: %.loc10_20: {.x: ()} = struct_literal (%.loc10_19) +// CHECK:STDOUT: assign file.%c.var, +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- import_both.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: ()} [template] +// CHECK:STDOUT: %tuple: () = tuple_value () [template] +// CHECK:STDOUT: %struct: C = struct_value (%tuple) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .NS = %NS +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir1, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+11, loaded +// CHECK:STDOUT: %NS: = namespace %import_ref.2, [template] { +// CHECK:STDOUT: .NSC = %import_ref.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.3 = import_ref ir1, inst+12, unloaded +// CHECK:STDOUT: %import_ref.4 = import_ref ir1, inst+7, unloaded +// CHECK:STDOUT: %import_ref.5 = import_ref ir1, inst+2, unloaded +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C] +// CHECK:STDOUT: %c.var: ref C = var c +// CHECK:STDOUT: %c: ref C = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = file.%import_ref.4 +// CHECK:STDOUT: .Self = file.%import_ref.5 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc7_19.1: () = tuple_literal () +// CHECK:STDOUT: %.loc7_20.1: {.x: ()} = struct_literal (%.loc7_19.1) +// CHECK:STDOUT: %.loc7_20.2: ref () = class_element_access file.%c.var, element0 +// CHECK:STDOUT: %.loc7_19.2: init () = tuple_init () to %.loc7_20.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc7_20.3: init () = converted %.loc7_19.1, %.loc7_19.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc7_20.4: init C = class_init (%.loc7_20.3), file.%c.var [template = constants.%struct] +// CHECK:STDOUT: %.loc7_21: init C = converted %.loc7_20.1, %.loc7_20.4 [template = constants.%struct] +// CHECK:STDOUT: assign file.%c.var, %.loc7_21 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- import_both_reversed.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: ()} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: ()} [template] +// CHECK:STDOUT: %tuple: () = tuple_value () [template] +// CHECK:STDOUT: %struct: C = struct_value (%tuple) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .NS = %NS +// CHECK:STDOUT: .c = %c +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.2: = import_ref ir2, inst+11, loaded +// CHECK:STDOUT: %NS: = namespace %import_ref.2, [template] { +// CHECK:STDOUT: .NSC = %import_ref.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.3 = import_ref ir2, inst+12, unloaded +// CHECK:STDOUT: %import_ref.4 = import_ref ir2, inst+7, unloaded +// CHECK:STDOUT: %import_ref.5 = import_ref ir2, inst+2, unloaded +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C] +// CHECK:STDOUT: %c.var: ref C = var c +// CHECK:STDOUT: %c: ref C = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = file.%import_ref.4 +// CHECK:STDOUT: .Self = file.%import_ref.5 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc7_19.1: () = tuple_literal () +// CHECK:STDOUT: %.loc7_20.1: {.x: ()} = struct_literal (%.loc7_19.1) +// CHECK:STDOUT: %.loc7_20.2: ref () = class_element_access file.%c.var, element0 +// CHECK:STDOUT: %.loc7_19.2: init () = tuple_init () to %.loc7_20.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc7_20.3: init () = converted %.loc7_19.1, %.loc7_19.2 [template = constants.%tuple] +// CHECK:STDOUT: %.loc7_20.4: init C = class_init (%.loc7_20.3), file.%c.var [template = constants.%struct] +// CHECK:STDOUT: %.loc7_21: init C = converted %.loc7_20.1, %.loc7_20.4 [template = constants.%struct] +// CHECK:STDOUT: assign file.%c.var, %.loc7_21 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_use_non_export_class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Local = %Local +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.ref: = name_ref C, [template = ] +// CHECK:STDOUT: %Local: = bind_alias Local, [template = ] +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index ea57a11fc212..909ecdd61308 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -99,6 +99,7 @@ CARBON_DIAGNOSTIC_KIND(ExpectedLibraryName) CARBON_DIAGNOSTIC_KIND(ExpectedLibraryNameOrDefault) CARBON_DIAGNOSTIC_KIND(MissingLibraryKeyword) CARBON_DIAGNOSTIC_KIND(ExpectedApiOrImpl) +CARBON_DIAGNOSTIC_KIND(ExportImportPackage) // For-specific diagnostics. CARBON_DIAGNOSTIC_KIND(ExpectedIn) @@ -148,6 +149,7 @@ CARBON_DIAGNOSTIC_KIND(ImportSelf) CARBON_DIAGNOSTIC_KIND(ExplicitImportApi) CARBON_DIAGNOSTIC_KIND(RepeatedImport) CARBON_DIAGNOSTIC_KIND(FirstImported) +CARBON_DIAGNOSTIC_KIND(ExportFromImpl) // Merge-related redeclaration checking. CARBON_DIAGNOSTIC_KIND(RedeclPrevDecl) diff --git a/toolchain/lex/token_kind.def b/toolchain/lex/token_kind.def index 502836fe5e5a..341506a878a8 100644 --- a/toolchain/lex/token_kind.def +++ b/toolchain/lex/token_kind.def @@ -159,6 +159,7 @@ CARBON_KEYWORD_TOKEN(Continue, "continue") CARBON_KEYWORD_TOKEN(Default, "default") CARBON_KEYWORD_TOKEN(Destructor, "destructor") CARBON_KEYWORD_TOKEN(Else, "else") +CARBON_KEYWORD_TOKEN(Export, "export") CARBON_KEYWORD_TOKEN(Extend, "extend") CARBON_KEYWORD_TOKEN(Extern, "extern") CARBON_KEYWORD_TOKEN(False, "false") diff --git a/toolchain/parse/handle_decl_scope_loop.cpp b/toolchain/parse/handle_decl_scope_loop.cpp index 1d549de557ae..748a7466faf3 100644 --- a/toolchain/parse/handle_decl_scope_loop.cpp +++ b/toolchain/parse/handle_decl_scope_loop.cpp @@ -19,7 +19,11 @@ static auto TryHandleEndOrPackagingDecl(Context& context) -> bool { context.PopAndDiscardState(); return true; } - // `import`, `library`, and `package` manage their packaging state. + // Packaging-related keywords manage their packaging state. + case Lex::TokenKind::Export: { + context.PushState(State::Export); + return true; + } case Lex::TokenKind::Import: { context.PushState(State::Import); return true; @@ -32,7 +36,7 @@ static auto TryHandleEndOrPackagingDecl(Context& context) -> bool { context.PushState(State::Package); return true; } - default: { + default: // Because a non-packaging keyword was encountered, packaging is complete. // Misplaced packaging keywords may lead to this being re-triggered. if (context.packaging_state() != @@ -44,7 +48,6 @@ static auto TryHandleEndOrPackagingDecl(Context& context) -> bool { Context::PackagingState::AfterNonPackagingDecl); } return false; - } } } @@ -124,6 +127,11 @@ static auto TryHandleAsDecl(Context& context, Context::StateStackEntry state, HandleBaseAsDecl(context, state); return true; } + case Lex::TokenKind::Choice: { + ApplyIntroducer(context, state, NodeKind::ChoiceIntroducer, + State::ChoiceIntroducer); + return true; + } case Lex::TokenKind::Class: { ApplyIntroducer(context, state, NodeKind::ClassIntroducer, State::TypeAfterIntroducerAsClass); @@ -168,11 +176,6 @@ static auto TryHandleAsDecl(Context& context, Context::StateStackEntry state, State::VarAsDecl); return true; } - case Lex::TokenKind::Choice: { - ApplyIntroducer(context, state, NodeKind::ChoiceIntroducer, - State::ChoiceIntroducer); - return true; - } case Lex::TokenKind::Semi: { if (saw_modifier) { diff --git a/toolchain/parse/handle_import_and_package.cpp b/toolchain/parse/handle_import_and_package.cpp index 59b098bb7eb6..e26d34aeb2b4 100644 --- a/toolchain/parse/handle_import_and_package.cpp +++ b/toolchain/parse/handle_import_and_package.cpp @@ -73,14 +73,23 @@ static auto HandleApiOrImpl(Context& context) // Handles everything after the directive's introducer. static auto HandleDirectiveContent(Context& context, Context::StateStackEntry state, - NodeKind directive, + NodeKind directive, bool is_export, llvm::function_ref on_parse_error) -> void { Tree::PackagingNames names{ - .node_id = ImportDirectiveId(NodeId(state.subtree_start))}; + .node_id = ImportDirectiveId(NodeId(state.subtree_start)), + .is_export = is_export}; if (directive != NodeKind::LibraryDirective) { if (auto package_name_token = context.ConsumeIf(Lex::TokenKind::Identifier)) { + if (names.is_export) { + names.is_export = false; + state.has_error = true; + + CARBON_DIAGNOSTIC(ExportImportPackage, Error, + "`export` cannot be used when importing a package."); + context.emitter().Emit(*package_name_token, ExportImportPackage); + } names.package_id = context.tokens().GetIdentifier(*package_name_token); context.AddLeafNode(NodeKind::PackageName, *package_name_token); } else if (directive == NodeKind::PackageDirective || @@ -157,47 +166,104 @@ static auto HandleDirectiveContent(Context& context, } } -auto HandleImport(Context& context) -> void { - auto state = context.PopState(); +// Returns true if currently in a valid state for imports, false otherwise. May +// update the packaging state respectively. +static auto VerifyInImports(Context& context, Lex::TokenIndex intro_token) + -> bool { + switch (context.packaging_state()) { + case Context::PackagingState::FileStart: + // `package` is no longer allowed, but `import` may repeat. + context.set_packaging_state(Context::PackagingState::InImports); + return true; + case Context::PackagingState::InImports: + return true; + + case Context::PackagingState::AfterNonPackagingDecl: { + context.set_packaging_state( + Context::PackagingState::InImportsAfterNonPackagingDecl); + CARBON_DIAGNOSTIC(ImportTooLate, Error, + "`import` and `export` directives must come after the " + "`package` directive (if present) and before any other " + "entities in the file."); + CARBON_DIAGNOSTIC(FirstDecl, Note, "First declaration is here."); + context.emitter() + .Build(intro_token, ImportTooLate) + .Note(context.first_non_packaging_token(), FirstDecl) + .Emit(); + return false; + } + + case Context::PackagingState::InImportsAfterNonPackagingDecl: + // There is a sequential block of misplaced `import` statements, which can + // occur if a declaration is added above `import`s. Avoid duplicate + // warnings. + return false; + } +} + +// Common logic for both `import` and `export import`, distinguished by whether +// `export_token` is valid. +static auto HandleImportHelper(Context& context, + const Context::StateStackEntry& state, + Lex::TokenIndex export_token) -> void { auto directive = NodeKind::ImportDirective; auto on_parse_error = [&] { OnParseError(context, state, directive); }; auto intro_token = context.ConsumeChecked(Lex::TokenKind::Import); context.AddLeafNode(NodeKind::ImportIntroducer, intro_token); - switch (context.packaging_state()) { - case Context::PackagingState::FileStart: - // `package` is no longer allowed, but `import` may repeat. - context.set_packaging_state(Context::PackagingState::InImports); - [[fallthrough]]; - - case Context::PackagingState::InImports: - HandleDirectiveContent(context, state, directive, on_parse_error); - break; - - case Context::PackagingState::AfterNonPackagingDecl: { - context.set_packaging_state( - Context::PackagingState::InImportsAfterNonPackagingDecl); - CARBON_DIAGNOSTIC( - ImportTooLate, Error, - "`import` directives must come after the `package` directive (if " - "present) and before any other entities in the file."); - CARBON_DIAGNOSTIC(FirstDecl, Note, "First declaration is here."); - context.emitter() - .Build(intro_token, ImportTooLate) - .Note(context.first_non_packaging_token(), FirstDecl) - .Emit(); - on_parse_error(); - break; - } - case Context::PackagingState::InImportsAfterNonPackagingDecl: - // There is a sequential block of misplaced `import` statements, which can - // occur if a declaration is added above `import`s. Avoid duplicate - // warnings. - on_parse_error(); - break; + if (export_token.is_valid()) { + context.AddLeafNode(NodeKind::ImportExport, export_token); } + + if (VerifyInImports(context, intro_token)) { + HandleDirectiveContent(context, state, directive, export_token.is_valid(), + on_parse_error); + } else { + on_parse_error(); + } +} + +auto HandleExport(Context& context) -> void { + auto state = context.PopState(); + + context.ConsumeChecked(Lex::TokenKind::Export); + + // Error for both Main//default and every implementation file. + auto packaging = context.tree().packaging_directive(); + if (!packaging || packaging->api_or_impl == Tree::ApiOrImpl::Impl) { + CARBON_DIAGNOSTIC(ExportFromImpl, Error, + "`export` is only allowed in API files."); + context.emitter().Emit(state.token, ExportFromImpl); + state.has_error = true; + } + + if (context.PositionIs(Lex::TokenKind::Import)) { + HandleImportHelper(context, state, state.token); + } else { + if (!VerifyInImports(context, state.token)) { + state.has_error = true; + } + context.AddLeafNode(NodeKind::ExportIntroducer, state.token, + state.has_error); + context.PushState(state, State::ExportFinish); + context.PushState(State::DeclNameAndParamsAsNone, state.token); + } +} + +auto HandleExportFinish(Context& context) -> void { + auto state = context.PopState(); + + context.AddNodeExpectingDeclSemi(state, NodeKind::ExportDirective, + Lex::TokenKind::Export, + /*is_def_allowed=*/false); +} + +auto HandleImport(Context& context) -> void { + auto state = context.PopState(); + + HandleImportHelper(context, state, /*export_token=*/Lex::TokenIndex::Invalid); } // Handles common logic for `package` and `library`. @@ -229,7 +295,8 @@ static auto HandlePackageAndLibraryDirectives(Context& context, // `package`/`library` is no longer allowed, but `import` may repeat. context.set_packaging_state(Context::PackagingState::InImports); - HandleDirectiveContent(context, state, directive, on_parse_error); + HandleDirectiveContent(context, state, directive, /*is_export=*/false, + on_parse_error); } auto HandlePackage(Context& context) -> void { diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 71562902b68a..e2e93050682f 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -194,12 +194,15 @@ CARBON_PARSE_NODE_KIND_BRACKET(PackageDirective, PackageIntroducer, // `import`: // ImportIntroducer +// _optional_: ImportExport // _optional_ _external_: PackageName // _optional_ _external_: LibrarySpecifier // ImportDirective CARBON_PARSE_NODE_KIND_CHILD_COUNT(ImportIntroducer, 0, Import) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ImportExport, 0, Export) CARBON_PARSE_NODE_KIND_BRACKET(ImportDirective, ImportIntroducer, CARBON_IF_VALID(Semi)) + // `library` as directive: // LibraryIntroducer // DefaultLibrary or _external_: LibraryName @@ -215,6 +218,13 @@ CARBON_PARSE_NODE_KIND_BRACKET(LibraryDirective, LibraryIntroducer, // LibrarySpecifier CARBON_PARSE_NODE_KIND_CHILD_COUNT(LibrarySpecifier, 1, Library) +// `export`: +// ExportIntroducer +// _external_: IdentifierName or QualifiedName +// ExportDirective +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ExportIntroducer, 0, Export) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ExportDirective, 2, CARBON_IF_VALID(Semi)) + // `namespace`: // NamespaceStart // _repeated_ _external_: modifier diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index 036054e57af7..8c172555b92c 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -670,12 +670,33 @@ CARBON_PARSE_STATE(FunctionSignatureFinish) // (state done) CARBON_PARSE_STATE(FunctionDefinitionFinish) +// Handles `export`. +// +// export import library "libname"; +// ^~~~~~ +// 1. Import +// +// export Name; +// ^~~~~~ +// 1. DeclNameAndParamsAsNone +// 2. ExportFinish +CARBON_PARSE_STATE(Export) + +// Finishes an `export`. +// +// export Name; +// ^ +// (state done) +CARBON_PARSE_STATE(ExportFinish) + // Handles `import`. // // import pkgname [library "libname"] ; // ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // import ??? ; // ^~~~~~~~~~~~ +// export import library "libname"; +// ^~~~~~~~~~~~~~~~~~~~~~~~~ // (state done) CARBON_PARSE_STATE(Import) diff --git a/toolchain/parse/testdata/packages/export.carbon b/toolchain/parse/testdata/packages/export.carbon new file mode 100644 index 000000000000..09c66a3d5eca --- /dev/null +++ b/toolchain/parse/testdata/packages/export.carbon @@ -0,0 +1,267 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- name.carbon + +package Pkg api; + +export Foo; + +// --- qual_name.carbon + +package Pkg api; + +export Foo.Bar; + +// --- fail_expr.carbon + +package Pkg api; + +// CHECK:STDERR: fail_expr.carbon:[[@LINE+4]]:8: ERROR: `export` introducer should be followed by a name. +// CHECK:STDERR: export (); +// CHECK:STDERR: ^ +// CHECK:STDERR: +export (); + +// --- fail_package_in_name.carbon + +package Pkg api; + +// CHECK:STDERR: fail_package_in_name.carbon:[[@LINE+4]]:8: ERROR: `export` introducer should be followed by a name. +// CHECK:STDERR: export package.Bar; +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: +export package.Bar; + +// --- fail_keyword_only + +package Pkg api; + +// CHECK:STDERR: fail_keyword_only:[[@LINE+4]]:1: ERROR: `export` introducer should be followed by a name. +// CHECK:STDERR: export +// CHECK:STDERR: ^~~~~~ +// CHECK:STDERR: +export + +// --- fail_no_name.carbon + +package Pkg api; + +// CHECK:STDERR: fail_no_name.carbon:[[@LINE+4]]:8: ERROR: `export` introducer should be followed by a name. +// CHECK:STDERR: export ; +// CHECK:STDERR: ^ +// CHECK:STDERR: +export ; + +// --- fail_no_semi.carbon + +package Pkg api; + +export Foo +// CHECK:STDERR: fail_no_semi.carbon:[[@LINE+4]]:1: ERROR: `export` declarations must end with a `;`. +// CHECK:STDERR: +// CHECK:STDERR: ^ +// CHECK:STDERR: + +// --- fail_incomplete_qual_name.carbon + +package Pkg api; + +// CHECK:STDERR: fail_incomplete_qual_name.carbon:[[@LINE+4]]:12: ERROR: Expected identifier after `.`. +// CHECK:STDERR: export Foo.; +// CHECK:STDERR: ^ +// CHECK:STDERR: +export Foo.; + +// --- fail_incomplete_qual_name2.carbon + +package Pkg api; + +// CHECK:STDERR: fail_incomplete_qual_name2.carbon:[[@LINE+4]]:8: ERROR: `export` introducer should be followed by a name. +// CHECK:STDERR: export .Bar; +// CHECK:STDERR: ^ +// CHECK:STDERR: +export .Bar; + +// --- fail_after_decl.carbon + +package Pkg api; + +class C; + +// CHECK:STDERR: fail_after_decl.carbon:[[@LINE+7]]:1: ERROR: `import` and `export` directives must come after the `package` directive (if present) and before any other entities in the file. +// CHECK:STDERR: export Foo; +// CHECK:STDERR: ^~~~~~ +// CHECK:STDERR: fail_after_decl.carbon:[[@LINE-5]]:1: First declaration is here. +// CHECK:STDERR: class C; +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: +export Foo; + +// --- fail_in_default_library.carbon + +// CHECK:STDERR: fail_in_default_library.carbon:[[@LINE+4]]:1: ERROR: `export` is only allowed in API files. +// CHECK:STDERR: export Foo; +// CHECK:STDERR: ^~~~~~ +// CHECK:STDERR: +export Foo; + +// --- fail_in_impl.carbon + +package Pkg impl; + +// CHECK:STDERR: fail_in_impl.carbon:[[@LINE+3]]:1: ERROR: `export` is only allowed in API files. +// CHECK:STDERR: export Foo; +// CHECK:STDERR: ^~~~~~ +export Foo; + +// CHECK:STDOUT: - filename: name.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'ExportDirective', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: qual_name.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Bar'}, +// CHECK:STDOUT: {kind: 'QualifiedName', text: '.', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExportDirective', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_expr.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '(', has_error: yes}, +// CHECK:STDOUT: {kind: 'ExportDirective', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_package_in_name.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: 'package', has_error: yes}, +// CHECK:STDOUT: {kind: 'ExportDirective', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_keyword_only +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '', has_error: yes}, +// CHECK:STDOUT: {kind: 'ExportDirective', text: 'export', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_no_name.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'ExportDirective', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_no_semi.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'ExportDirective', text: 'Foo', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_incomplete_qual_name.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'QualifiedName', text: '.', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExportDirective', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_incomplete_qual_name2.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '.', has_error: yes}, +// CHECK:STDOUT: {kind: 'ExportDirective', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_after_decl.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'C'}, +// CHECK:STDOUT: {kind: 'ClassDecl', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'ExportDirective', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_in_default_library.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'ExportDirective', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_in_impl.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageImpl', text: 'impl'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'ExportDirective', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/import/export.carbon b/toolchain/parse/testdata/packages/import/export.carbon new file mode 100644 index 000000000000..49f4ff28f8fe --- /dev/null +++ b/toolchain/parse/testdata/packages/import/export.carbon @@ -0,0 +1,147 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- fail_package.carbon + +package Pkg api; + +// CHECK:STDERR: fail_package.carbon:[[@LINE+4]]:15: ERROR: `export` cannot be used when importing a package. +// CHECK:STDERR: export import Bar; +// CHECK:STDERR: ^~~ +// CHECK:STDERR: +export import Bar; + +// --- default.carbon + +package Pkg api; + +export import library default; + +// --- named.carbon + +package Pkg api; + +export import library "lib"; + +// --- fail_order.carbon + +package Pkg api; + +export import library "a"; + +class C; + +// CHECK:STDERR: fail_order.carbon:[[@LINE+7]]:8: ERROR: `import` and `export` directives must come after the `package` directive (if present) and before any other entities in the file. +// CHECK:STDERR: export import library "b"; +// CHECK:STDERR: ^~~~~~ +// CHECK:STDERR: fail_order.carbon:[[@LINE-5]]:1: First declaration is here. +// CHECK:STDERR: class C; +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: +export import library "b"; + +// --- fail_in_default_library.carbon + +// CHECK:STDERR: fail_in_default_library.carbon:[[@LINE+4]]:1: ERROR: `export` is only allowed in API files. +// CHECK:STDERR: export import library "lib"; +// CHECK:STDERR: ^~~~~~ +// CHECK:STDERR: +export import library "lib"; + +// --- fail_in_impl.carbon + +package Pkg impl; + +// CHECK:STDERR: fail_in_impl.carbon:[[@LINE+3]]:1: ERROR: `export` is only allowed in API files. +// CHECK:STDERR: export import library "lib"; +// CHECK:STDERR: ^~~~~~ +export import library "lib"; + +// CHECK:STDOUT: - filename: fail_package.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'ImportExport', text: 'export'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Bar'}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: default.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'ImportExport', text: 'export'}, +// CHECK:STDOUT: {kind: 'DefaultLibrary', text: 'default'}, +// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: named.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'ImportExport', text: 'export'}, +// CHECK:STDOUT: {kind: 'LibraryName', text: '"lib"'}, +// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_order.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageApi', text: 'api'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'ImportExport', text: 'export'}, +// CHECK:STDOUT: {kind: 'LibraryName', text: '"a"'}, +// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'C'}, +// CHECK:STDOUT: {kind: 'ClassDecl', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'ImportExport', text: 'export'}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_in_default_library.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'ImportExport', text: 'export'}, +// CHECK:STDOUT: {kind: 'LibraryName', text: '"lib"'}, +// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_in_impl.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'PackageIntroducer', text: 'package'}, +// CHECK:STDOUT: {kind: 'PackageName', text: 'Pkg'}, +// CHECK:STDOUT: {kind: 'PackageImpl', text: 'impl'}, +// CHECK:STDOUT: {kind: 'PackageDirective', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ImportIntroducer', text: 'import'}, +// CHECK:STDOUT: {kind: 'ImportExport', text: 'export'}, +// CHECK:STDOUT: {kind: 'LibraryName', text: '"lib"'}, +// CHECK:STDOUT: {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ImportDirective', text: ';', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/packages/import/fail_after_decl.carbon b/toolchain/parse/testdata/packages/import/fail_after_decl.carbon index 02d98b5d68ea..977da44f6768 100644 --- a/toolchain/parse/testdata/packages/import/fail_after_decl.carbon +++ b/toolchain/parse/testdata/packages/import/fail_after_decl.carbon @@ -6,7 +6,7 @@ fn A(); -// CHECK:STDERR: fail_after_decl.carbon:[[@LINE+6]]:1: ERROR: `import` directives must come after the `package` directive (if present) and before any other entities in the file. +// CHECK:STDERR: fail_after_decl.carbon:[[@LINE+6]]:1: ERROR: `import` and `export` directives must come after the `package` directive (if present) and before any other entities in the file. // CHECK:STDERR: import B; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_after_decl.carbon:[[@LINE-5]]:1: First declaration is here. diff --git a/toolchain/parse/testdata/packages/import/fail_after_decl_repeated.carbon b/toolchain/parse/testdata/packages/import/fail_after_decl_repeated.carbon index 58d77b6486b5..9a6588c89130 100644 --- a/toolchain/parse/testdata/packages/import/fail_after_decl_repeated.carbon +++ b/toolchain/parse/testdata/packages/import/fail_after_decl_repeated.carbon @@ -6,7 +6,7 @@ fn A(); -// CHECK:STDERR: fail_after_decl_repeated.carbon:[[@LINE+7]]:1: ERROR: `import` directives must come after the `package` directive (if present) and before any other entities in the file. +// CHECK:STDERR: fail_after_decl_repeated.carbon:[[@LINE+7]]:1: ERROR: `import` and `export` directives must come after the `package` directive (if present) and before any other entities in the file. // CHECK:STDERR: import B; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_after_decl_repeated.carbon:[[@LINE-5]]:1: First declaration is here. @@ -28,7 +28,7 @@ package D; fn E(); -// CHECK:STDERR: fail_after_decl_repeated.carbon:[[@LINE+6]]:1: ERROR: `import` directives must come after the `package` directive (if present) and before any other entities in the file. +// CHECK:STDERR: fail_after_decl_repeated.carbon:[[@LINE+6]]:1: ERROR: `import` and `export` directives must come after the `package` directive (if present) and before any other entities in the file. // CHECK:STDERR: import F; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_after_decl_repeated.carbon:[[@LINE-27]]:1: First declaration is here. diff --git a/toolchain/parse/tree.h b/toolchain/parse/tree.h index c1d7b5ded491..acd6a6c3b9ab 100644 --- a/toolchain/parse/tree.h +++ b/toolchain/parse/tree.h @@ -92,6 +92,9 @@ class Tree : public Printable { ImportDirectiveId node_id; IdentifierId package_id = IdentifierId::Invalid; StringLiteralValueId library_id = StringLiteralValueId::Invalid; + // Whether an import is exported. This is on the file's packaging directive + // even though it doesn't apply, for consistency in structure. + bool is_export = false; }; // The file's packaging. diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index 431f93971841..1bdfdf794cbe 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -143,8 +143,8 @@ struct QualifiedName { IdentifierNameId rhs; }; -// Library, package, import -// ------------------------ +// Library, package, import, export +// -------------------------------- // The `package` keyword in an expression. using PackageExpr = LeafNode; @@ -179,11 +179,13 @@ struct PackageDirective { // `import TheirPackage library "TheirLibrary";` using ImportIntroducer = LeafNode; +using ImportExport = LeafNode; struct ImportDirective { static constexpr auto Kind = NodeKind::ImportDirective.Define(NodeCategory::Decl); ImportIntroducerId introducer; + std::optional export_modifier; std::optional name; std::optional library; }; @@ -199,6 +201,16 @@ struct LibraryDirective { NodeIdOneOf api_or_impl; }; +// `export` as a directive. +using ExportIntroducer = LeafNode; +struct ExportDirective { + static constexpr auto Kind = + NodeKind::ExportDirective.Define(NodeCategory::Decl); + + ExportIntroducerId introducer; + AnyNameComponentId name; +}; + // Namespace nodes // --------------- @@ -210,7 +222,7 @@ struct Namespace { NamespaceStartId introducer; llvm::SmallVector modifiers; - NodeIdOneOf name; + AnyNameComponentId name; }; // Pattern nodes diff --git a/toolchain/sem_ir/import_ir.h b/toolchain/sem_ir/import_ir.h index 97cce05ef6e6..8c4c5550451f 100644 --- a/toolchain/sem_ir/import_ir.h +++ b/toolchain/sem_ir/import_ir.h @@ -19,6 +19,8 @@ struct ImportIR : public Printable { Parse::ImportDirectiveId node_id; // The imported IR. const File* sem_ir; + // True if this is part of an `export import`. + bool is_export; }; // A reference to an instruction in an imported IR. Used for diagnostics with