From 3f972b5e4af1431f68c375a919eb1bb03cafa6da Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 10 Oct 2017 22:35:27 +0000 Subject: [PATCH] [Modules TS] Diagnose attempts to enter module implementation units without the module interface being available. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@315381 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 3 +++ include/clang/Sema/Sema.h | 7 ++++--- lib/Parse/Parser.cpp | 4 ++-- lib/Sema/SemaDecl.cpp | 12 +++++++----- .../basic/basic.link/module-declaration.cpp | 11 ++++++----- .../dcl.dcl/dcl.module/dcl.module.import/p1.cpp | 5 ++++- .../dcl.dcl/dcl.module/dcl.module.interface/p1.cpp | 11 ++++++----- test/CXX/modules-ts/dcl.dcl/dcl.module/p2.cpp | 6 ++++++ 8 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 test/CXX/modules-ts/dcl.dcl/dcl.module/p2.cpp diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 3479be934a..c26da79777 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -8994,6 +8994,9 @@ def err_module_redefinition : Error< "redefinition of module '%0'">; def note_prev_module_definition : Note<"previously defined here">; def note_prev_module_definition_from_ast_file : Note<"module loaded from '%0'">; +def err_module_not_defined : Error< + "definition of module '%0' is not available; use -fmodule-file= to specify " + "path to precompiled module interface">; def err_module_private_specialization : Error< "%select{template|partial|member}0 specialization cannot be " "declared __module_private__">; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 277c62f543..368ae18bab 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1533,7 +1533,8 @@ private: TypeDiagnoser *Diagnoser); struct ModuleScope { - clang::Module *Module; + clang::Module *Module = nullptr; + bool ModuleInterface = false; VisibleModuleSet OuterVisibleModules; }; /// The modules we're currently parsing. @@ -2051,9 +2052,9 @@ public: SourceLocation SemiLoc); enum class ModuleDeclKind { - Module, ///< 'module X;' + Interface, ///< 'export module X;' + Implementation, ///< 'module X;' Partition, ///< 'module partition X;' - Implementation, ///< 'module implementation X;' }; /// The parser has processed a module-declaration that begins the definition diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 682c481107..e24735dfc6 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -2048,7 +2048,7 @@ Parser::DeclGroupPtrTy Parser::ParseModuleDecl() { SourceLocation StartLoc = Tok.getLocation(); Sema::ModuleDeclKind MDK = TryConsumeToken(tok::kw_export) - ? Sema::ModuleDeclKind::Module + ? Sema::ModuleDeclKind::Interface : Sema::ModuleDeclKind::Implementation; assert(Tok.is(tok::kw_module) && "not a module declaration"); @@ -2057,7 +2057,7 @@ Parser::DeclGroupPtrTy Parser::ParseModuleDecl() { if (Tok.is(tok::identifier) && NextToken().is(tok::identifier) && Tok.getIdentifierInfo()->isStr("partition")) { // If 'partition' is present, this must be a module interface unit. - if (MDK != Sema::ModuleDeclKind::Module) + if (MDK != Sema::ModuleDeclKind::Interface) Diag(Tok.getLocation(), diag::err_module_implementation_partition) << FixItHint::CreateInsertion(ModuleLoc, "export "); MDK = Sema::ModuleDeclKind::Partition; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index a74cb57eab..84cae84dec 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -16168,6 +16168,7 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, // implementation unit. That indicates the 'export' is missing. Diag(ModuleLoc, diag::err_module_interface_implementation_mismatch) << FixItHint::CreateInsertion(ModuleLoc, "export "); + MDK = ModuleDeclKind::Interface; break; case LangOptions::CMK_ModuleMap: @@ -16207,7 +16208,7 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, assert(ModuleScopes.size() == 1 && "expected to be at global module scope"); switch (MDK) { - case ModuleDeclKind::Module: { + case ModuleDeclKind::Interface: { // We can't have parsed or imported a definition of this module or parsed a // module map defining it already. if (auto *M = Map.findModule(ModuleName)) { @@ -16237,14 +16238,16 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, PP.getIdentifierInfo(ModuleName), Path[0].second); Mod = getModuleLoader().loadModule(ModuleLoc, Path, Module::AllVisible, /*IsIncludeDirective=*/false); - // FIXME: Produce an error in this case. - if (!Mod) + if (!Mod) { + Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName; return nullptr; + } break; } // Switch from the global module to the named module. ModuleScopes.back().Module = Mod; + ModuleScopes.back().ModuleInterface = MDK != ModuleDeclKind::Implementation; VisibleModules.setVisible(Mod, ModuleLoc); // From now on, we have an owning module for all declarations we see. @@ -16430,8 +16433,7 @@ Decl *Sema::ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc, // C++ Modules TS draft: // An export-declaration shall appear in the purview of a module other than // the global module. - if (ModuleScopes.empty() || - ModuleScopes.back().Module->Kind != Module::ModuleInterfaceUnit) + if (ModuleScopes.empty() || !ModuleScopes.back().ModuleInterface) Diag(ExportLoc, diag::err_export_not_in_module_interface); // An export-declaration [...] shall not contain more than one diff --git a/test/CXX/modules-ts/basic/basic.link/module-declaration.cpp b/test/CXX/modules-ts/basic/basic.link/module-declaration.cpp index 3c0ac5d2a1..feb0afdda0 100644 --- a/test/CXX/modules-ts/basic/basic.link/module-declaration.cpp +++ b/test/CXX/modules-ts/basic/basic.link/module-declaration.cpp @@ -9,7 +9,6 @@ // RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface -fmodule-file=%t/x.pcm %t/x.y.cppm -o %t/x.y.pcm // // Module implementation for unknown and known module. (The former is ill-formed.) -// FIXME: TEST=1 should fail because we don't have an interface for module z. // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ // RUN: -DTEST=1 -DEXPORT= -DPARTITION= -DMODULE_NAME=z // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ @@ -32,11 +31,11 @@ // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ // RUN: -DTEST=7 -DEXPORT= -DPARTITION=elderberry -DMODULE_NAME=z // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ -// RUN: -DTEST=8 -DEXPORT= -DPARTITION= -DMODULE_NAME='z [[]]' +// RUN: -DTEST=8 -DEXPORT=export -DPARTITION= -DMODULE_NAME='z [[]]' // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ -// RUN: -DTEST=9 -DEXPORT= -DPARTITION= -DMODULE_NAME='z [[fancy]]' +// RUN: -DTEST=9 -DEXPORT=export -DPARTITION= -DMODULE_NAME='z [[fancy]]' // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ -// RUN: -DTEST=10 -DEXPORT= -DPARTITION= -DMODULE_NAME='z [[maybe_unused]]' +// RUN: -DTEST=10 -DEXPORT=export -DPARTITION= -DMODULE_NAME='z [[maybe_unused]]' EXPORT module PARTITION MODULE_NAME; #if TEST == 4 @@ -45,11 +44,13 @@ EXPORT module PARTITION MODULE_NAME; #elif TEST == 6 // expected-error@-5 {{module partition must be declared 'export'}} #elif TEST == 7 -// expected-error@-7 {{expected ';'}} expected-error@-7 {{requires a type specifier}} +// expected-error@-7 {{expected ';'}} expected-error@-7 {{requires a type specifier}} expected-error@-7 {{definition of module 'elderberry' is not available}} #elif TEST == 9 // expected-warning@-9 {{unknown attribute 'fancy' ignored}} #elif TEST == 10 // expected-error-re@-11 {{'maybe_unused' attribute cannot be applied to a module{{$}}}} +#elif TEST == 1 +// expected-error@-13 {{definition of module 'z' is not available}} #else // expected-no-diagnostics #endif diff --git a/test/CXX/modules-ts/dcl.dcl/dcl.module/dcl.module.import/p1.cpp b/test/CXX/modules-ts/dcl.dcl/dcl.module/dcl.module.import/p1.cpp index dc83ddad97..15900c1f6a 100644 --- a/test/CXX/modules-ts/dcl.dcl/dcl.module/dcl.module.import/p1.cpp +++ b/test/CXX/modules-ts/dcl.dcl/dcl.module/dcl.module.import/p1.cpp @@ -9,12 +9,15 @@ // RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %t/a.b.cppm -o %t/a.b.pcm // // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -fmodule-file=%t/a.b.pcm -verify %s \ -// RUN: -DMODULE_NAME=z +// RUN: -DMODULE_NAME=z -DINTERFACE // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -fmodule-file=%t/a.b.pcm -verify %s \ // RUN: -DMODULE_NAME=a.b // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -fmodule-file=%t/a.b.pcm -verify %s \ // RUN: -DMODULE_X -DMODULE_NAME=x +#ifdef INTERFACE +export +#endif module MODULE_NAME; int use_1 = a; diff --git a/test/CXX/modules-ts/dcl.dcl/dcl.module/dcl.module.interface/p1.cpp b/test/CXX/modules-ts/dcl.dcl/dcl.module/dcl.module.interface/p1.cpp index afe3a7a485..0ec1a90e1a 100644 --- a/test/CXX/modules-ts/dcl.dcl/dcl.module/dcl.module.interface/p1.cpp +++ b/test/CXX/modules-ts/dcl.dcl/dcl.module/dcl.module.interface/p1.cpp @@ -1,17 +1,19 @@ // RUN: %clang_cc1 -fmodules-ts %s -verify -o /dev/null -// RUN: %clang_cc1 -fmodules-ts %s -DINTERFACE -verify -o /dev/null -// RUN: %clang_cc1 -fmodules-ts %s -DIMPLEMENTATION -verify -o /dev/null +// RUN: %clang_cc1 -fmodules-ts %s -DINTERFACE -verify -emit-module-interface -o %t +// RUN: %clang_cc1 -fmodules-ts %s -DIMPLEMENTATION -verify -fmodule-file=%t -o /dev/null // // RUN: %clang_cc1 -fmodules-ts %s -DBUILT_AS_INTERFACE -emit-module-interface -verify -o /dev/null // RUN: %clang_cc1 -fmodules-ts %s -DINTERFACE -DBUILT_AS_INTERFACE -emit-module-interface -verify -o /dev/null // RUN: %clang_cc1 -fmodules-ts %s -DIMPLEMENTATION -DBUILT_AS_INTERFACE -emit-module-interface -verify -o /dev/null #if INTERFACE +// expected-no-diagnostics export module A; #elif IMPLEMENTATION module A; #ifdef BUILT_AS_INTERFACE // expected-error@-2 {{missing 'export' specifier in module declaration while building module interface}} + #define INTERFACE #endif #else #ifdef BUILT_AS_INTERFACE @@ -19,9 +21,8 @@ module A; #endif #endif -export int a; #ifndef INTERFACE -// expected-error@-2 {{export declaration can only be used within a module interface unit}} +export int b; // expected-error {{export declaration can only be used within a module interface unit}} #else -// expected-no-diagnostics +export int a; #endif diff --git a/test/CXX/modules-ts/dcl.dcl/dcl.module/p2.cpp b/test/CXX/modules-ts/dcl.dcl/dcl.module/p2.cpp new file mode 100644 index 0000000000..992715e6b1 --- /dev/null +++ b/test/CXX/modules-ts/dcl.dcl/dcl.module/p2.cpp @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -fmodules-ts -verify %s + +// A named module shall contain exactly one module interface unit. +module M; // expected-error {{definition of module 'M' is not available; use -fmodule-file= to specify path to precompiled module interface}} + +// FIXME: How do we ensure there is not more than one? -- 2.40.0