Skip to content

Commit 241559d

Browse files
committed
[Serialization] Add an option to output modules regardless of errors
Adds a new frontend option "-experimental-allow-module-with-compiler-errors". If any compilation errors occur while generating the .swiftmodule, this mode will skip SIL entirely and only serialize the (likey invalid) AST. This existence of this option during generation is serialized into the resulting .swiftmodule. Errors found in deserialization are only allowed if it is set. Primarily intended for IDE requests (eg. indexing and code completion) to ensure robust cross-module results, despite possible errors. Resolves rdar://69815975
1 parent b714a31 commit 241559d

21 files changed

+386
-22
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,10 @@ namespace swift {
367367
/// TODO: remove this when @_implementationOnly modules are robust enough.
368368
bool AllowDeserializingImplementationOnly = false;
369369

370+
// Allow errors during module generation. See corresponding option in
371+
// FrontendOptions.
372+
bool AllowModuleWithCompilerErrors = false;
373+
370374
/// Sets the target we are building for and updates platform conditions
371375
/// to match.
372376
///

include/swift/Frontend/FrontendOptions.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,12 @@ class FrontendOptions {
289289
/// any merge-modules jobs.
290290
bool EnableExperimentalCrossModuleIncrementalBuild = false;
291291

292+
/// Best effort to output a .swiftmodule regardless of any compilation
293+
/// errors. SIL generation and serialization is skipped entirely when there
294+
/// are errors. The resulting serialized AST may include errors types and
295+
/// skip nodes entirely, depending on the errors involved.
296+
bool AllowModuleWithCompilerErrors = false;
297+
292298
/// The different modes for validating TBD against the LLVM IR.
293299
enum class TBDValidationMode {
294300
Default, ///< Do the default validation for the current platform.

include/swift/Option/FrontendOptions.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,4 +779,9 @@ def experimental_skip_all_function_bodies:
779779
Flag<["-"], "experimental-skip-all-function-bodies">,
780780
HelpText<"Skip type-checking function bodies and all SIL generation">;
781781

782+
def experimental_allow_module_with_compiler_errors:
783+
Flag<["-"], "experimental-allow-module-with-compiler-errors">,
784+
Flags<[HelpHidden]>,
785+
HelpText<"Attempt to output .swiftmodule, regardless of compilation errors">;
786+
782787
} // end let Flags = [FrontendOption, NoDriverOption, HelpHidden]

include/swift/Serialization/Validation.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ class ExtendedValidationInfo {
9898
unsigned IsSIB : 1;
9999
unsigned IsTestable : 1;
100100
unsigned ResilienceStrategy : 2;
101-
unsigned IsImplicitDynamicEnabled: 1;
101+
unsigned IsImplicitDynamicEnabled : 1;
102+
unsigned IsAllowModuleWithCompilerErrorsEnabled : 1;
102103
} Bits;
103104
public:
104105
ExtendedValidationInfo() : Bits() {}
@@ -138,6 +139,12 @@ class ExtendedValidationInfo {
138139
void setResilienceStrategy(ResilienceStrategy resilience) {
139140
Bits.ResilienceStrategy = unsigned(resilience);
140141
}
142+
bool isAllowModuleWithCompilerErrorsEnabled() {
143+
return Bits.IsAllowModuleWithCompilerErrorsEnabled;
144+
}
145+
void setAllowModuleWithCompilerErrorsEnabled(bool val) {
146+
Bits.IsAllowModuleWithCompilerErrorsEnabled = val;
147+
}
141148
};
142149

143150
/// Returns info about the serialized AST in the given data.

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ bool ArgsToFrontendOptionsConverter::convert(
214214
Opts.EnableIncrementalDependencyVerifier |= Args.hasArg(OPT_verify_incremental_dependencies);
215215
Opts.UseSharedResourceFolder = !Args.hasArg(OPT_use_static_resource_dir);
216216
Opts.DisableBuildingInterface = Args.hasArg(OPT_disable_building_interface);
217+
Opts.AllowModuleWithCompilerErrors = Args.hasArg(OPT_experimental_allow_module_with_compiler_errors);
217218

218219
computeImportObjCHeaderOptions();
219220
computeImplicitImportModuleNames(OPT_import_module, /*isTestable=*/false);

lib/Frontend/CompilerInvocation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
683683
Opts.DisableAvailabilityChecking = true;
684684
}
685685

686+
if (FrontendOpts.AllowModuleWithCompilerErrors) {
687+
Opts.AllowModuleWithCompilerErrors = true;
688+
}
689+
686690
return HadError || UnsupportedOS || UnsupportedArch;
687691
}
688692

lib/Frontend/Frontend.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1142,7 +1142,8 @@ static void countStatsPostSILOpt(UnifiedStatsReporter &Stats,
11421142
}
11431143

11441144
bool CompilerInstance::performSILProcessing(SILModule *silModule) {
1145-
if (performMandatorySILPasses(Invocation, silModule))
1145+
if (performMandatorySILPasses(Invocation, silModule) &&
1146+
!Invocation.getFrontendOptions().AllowModuleWithCompilerErrors)
11461147
return true;
11471148

11481149
{

lib/FrontendTool/FrontendTool.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1786,11 +1786,13 @@ static void performEndOfPipelineActions(CompilerInstance &Instance) {
17861786
assert(ctx.getLoadedModules().begin()->second == Instance.getMainModule());
17871787
}
17881788

1789-
// Verify the AST for all the modules we've loaded.
1790-
ctx.verifyAllLoadedModules();
1789+
if (!opts.AllowModuleWithCompilerErrors) {
1790+
// Verify the AST for all the modules we've loaded.
1791+
ctx.verifyAllLoadedModules();
17911792

1792-
// Verify generic signatures if we've been asked to.
1793-
verifyGenericSignaturesIfNeeded(Invocation, ctx);
1793+
// Verify generic signatures if we've been asked to.
1794+
verifyGenericSignaturesIfNeeded(Invocation, ctx);
1795+
}
17941796

17951797
// Emit any additional outputs that we only need for a successful compilation.
17961798
// We don't want to unnecessarily delay getting any errors back to the user.
@@ -1867,7 +1869,8 @@ withSemanticAnalysis(CompilerInstance &Instance, FrontendObserver *observer,
18671869

18681870
(void)migrator::updateCodeAndEmitRemapIfNeeded(&Instance);
18691871

1870-
if (Instance.getASTContext().hadError())
1872+
if (Instance.getASTContext().hadError() &&
1873+
!opts.AllowModuleWithCompilerErrors)
18711874
return true;
18721875

18731876
return cont(Instance);
@@ -2048,7 +2051,8 @@ static bool performCompile(CompilerInstance &Instance,
20482051
if (Instance.hasASTContext() &&
20492052
FrontendOptions::doesActionPerformEndOfPipelineActions(Action)) {
20502053
performEndOfPipelineActions(Instance);
2051-
hadError |= Instance.getASTContext().hadError();
2054+
if (!opts.AllowModuleWithCompilerErrors)
2055+
hadError |= Instance.getASTContext().hadError();
20522056
}
20532057
return hadError;
20542058
}
@@ -2334,7 +2338,7 @@ static bool performCompileStepsPostSILGen(CompilerInstance &Instance,
23342338
if (PSPs.haveModuleOrModuleDocOutputPaths()) {
23352339
if (Action == FrontendOptions::ActionType::MergeModules ||
23362340
Action == FrontendOptions::ActionType::EmitModuleOnly) {
2337-
return Context.hadError();
2341+
return Context.hadError() && !opts.AllowModuleWithCompilerErrors;
23382342
}
23392343
}
23402344

@@ -2352,7 +2356,7 @@ static bool performCompileStepsPostSILGen(CompilerInstance &Instance,
23522356

23532357
// Check if we had any errors; if we did, don't proceed to IRGen.
23542358
if (Context.hadError())
2355-
return true;
2359+
return !opts.AllowModuleWithCompilerErrors;
23562360

23572361
runSILLoweringPasses(*SM);
23582362

lib/SILGen/SILGen.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1952,6 +1952,11 @@ ASTLoweringRequest::evaluate(Evaluator &evaluator,
19521952
if (desc.opts.SkipFunctionBodies == FunctionBodySkipping::All)
19531953
return silMod;
19541954

1955+
// Skip emitting SIL if there's been any compilation errors
1956+
if (silMod->getASTContext().hadError() &&
1957+
silMod->getASTContext().LangOpts.AllowModuleWithCompilerErrors)
1958+
return silMod;
1959+
19551960
SILGenModuleRAII scope(*silMod);
19561961

19571962
// Emit a specific set of SILDeclRefs if needed.

lib/Sema/TypeCheckStorage.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2308,6 +2308,8 @@ IsAccessorTransparentRequest::evaluate(Evaluator &evaluator,
23082308
// Anything else should not have a synthesized setter.
23092309
LLVM_FALLTHROUGH;
23102310
case WriteImplKind::Immutable:
2311+
if (accessor->getASTContext().LangOpts.AllowModuleWithCompilerErrors)
2312+
return false;
23112313
llvm_unreachable("should not be synthesizing accessor in this case");
23122314

23132315
case WriteImplKind::StoredWithObservers:

lib/Serialization/DeclTypeRecordNodes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ TYPE(SIL_BLOCK_STORAGE)
109109
TYPE(SIL_BOX)
110110
TYPE(NAME_ALIAS)
111111

112+
TYPE(ERROR)
113+
112114
FIRST_DECL(TYPE_ALIAS, 60)
113115
DECL(GENERIC_TYPE_PARAM)
114116
DECL(ASSOCIATED_TYPE)

lib/Serialization/Deserialization.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2955,7 +2955,7 @@ class DeclDeserializer {
29552955
declOrOffset = param;
29562956

29572957
auto paramTy = MF.getType(interfaceTypeID);
2958-
if (paramTy->hasError()) {
2958+
if (paramTy->hasError() && !MF.isAllowModuleWithCompilerErrorsEnabled()) {
29592959
// FIXME: This should never happen, because we don't serialize
29602960
// error types.
29612961
DC->printContext(llvm::errs());
@@ -5670,6 +5670,23 @@ class TypeDeserializer {
56705670

56715671
return UnboundGenericType::get(genericDecl, parentTy, ctx);
56725672
}
5673+
5674+
Expected<Type> deserializeErrorType(ArrayRef<uint64_t> scratch,
5675+
StringRef blobData) {
5676+
if (!MF.isAllowModuleWithCompilerErrorsEnabled())
5677+
MF.fatal();
5678+
5679+
TypeID origID;
5680+
decls_block::ErrorTypeLayout::readRecord(scratch, origID);
5681+
5682+
auto origTy = MF.getTypeChecked(origID);
5683+
if (!origTy)
5684+
return origTy.takeError();
5685+
5686+
if (!origTy.get())
5687+
return ErrorType::get(ctx);
5688+
return ErrorType::get(origTy.get());
5689+
}
56735690
};
56745691
}
56755692

@@ -5693,7 +5710,8 @@ Expected<Type> ModuleFile::getTypeChecked(TypeID TID) {
56935710

56945711
#ifndef NDEBUG
56955712
PrettyStackTraceType trace(getContext(), "deserializing", typeOrOffset.get());
5696-
if (typeOrOffset.get()->hasError()) {
5713+
if (typeOrOffset.get()->hasError() &&
5714+
!isAllowModuleWithCompilerErrorsEnabled()) {
56975715
typeOrOffset.get()->dump(llvm::errs());
56985716
llvm_unreachable("deserialization produced an invalid type "
56995717
"(rdar://problem/30382791)");
@@ -5753,6 +5771,7 @@ Expected<Type> TypeDeserializer::getTypeCheckedImpl() {
57535771
CASE(Dictionary)
57545772
CASE(Optional)
57555773
CASE(UnboundGeneric)
5774+
CASE(Error)
57565775

57575776
#undef CASE
57585777

lib/Serialization/ModuleFile.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,12 @@ class ModuleFile
465465
return Core->Bits.IsImplicitDynamicEnabled;
466466
}
467467

468+
/// Whether this module is compiled while allowing errors
469+
/// ('-experimental-allow-module-with-compiler-errors').
470+
bool isAllowModuleWithCompilerErrorsEnabled() const {
471+
return Core->Bits.IsAllowModuleWithCompilerErrorsEnabled;
472+
}
473+
468474
/// \c true if this module has incremental dependency information.
469475
bool hasIncrementalInfo() const { return Core->hasIncrementalInfo(); }
470476

lib/Serialization/ModuleFileSharedCore.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ static bool readOptionsBlock(llvm::BitstreamCursor &cursor,
145145
options_block::ResilienceStrategyLayout::readRecord(scratch, Strategy);
146146
extendedInfo.setResilienceStrategy(ResilienceStrategy(Strategy));
147147
break;
148+
case options_block::IS_ALLOW_MODULE_WITH_COMPILER_ERRORS_ENABLED:
149+
extendedInfo.setAllowModuleWithCompilerErrorsEnabled(true);
150+
break;
148151
default:
149152
// Unknown options record, possibly for use by a future version of the
150153
// module format.
@@ -1150,6 +1153,8 @@ ModuleFileSharedCore::ModuleFileSharedCore(
11501153
Bits.IsTestable = extInfo.isTestable();
11511154
Bits.ResilienceStrategy = unsigned(extInfo.getResilienceStrategy());
11521155
Bits.IsImplicitDynamicEnabled = extInfo.isImplicitDynamicEnabled();
1156+
Bits.IsAllowModuleWithCompilerErrorsEnabled =
1157+
extInfo.isAllowModuleWithCompilerErrorsEnabled();
11531158
MiscVersion = info.miscVersion;
11541159

11551160
hasValidControlBlock = true;

lib/Serialization/ModuleFileSharedCore.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,11 @@ class ModuleFileSharedCore {
320320
/// Whether this module is compiled with implicit dynamic.
321321
unsigned IsImplicitDynamicEnabled: 1;
322322

323+
/// Whether this module is compiled while allowing errors.
324+
unsigned IsAllowModuleWithCompilerErrorsEnabled: 1;
325+
323326
// Explicitly pad out to the next word boundary.
324-
unsigned : 0;
327+
unsigned : 3;
325328
} Bits = {};
326329
static_assert(sizeof(ModuleBits) <= 8, "The bit set should be small");
327330

lib/Serialization/ModuleFormat.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5656
/// describe what change you made. The content of this comment isn't important;
5757
/// it just ensures a conflict if two people change the module format.
5858
/// Don't worry about adhering to the 80-column limit for this line.
59-
const uint16_t SWIFTMODULE_VERSION_MINOR = 585; // hop_to_executor instruction
59+
const uint16_t SWIFTMODULE_VERSION_MINOR = 586; // allow errors in modules
6060

6161
/// A standard hash seed used for all string hashes in a serialized module.
6262
///
@@ -787,7 +787,8 @@ namespace options_block {
787787
IS_TESTABLE,
788788
RESILIENCE_STRATEGY,
789789
ARE_PRIVATE_IMPORTS_ENABLED,
790-
IS_IMPLICIT_DYNAMIC_ENABLED
790+
IS_IMPLICIT_DYNAMIC_ENABLED,
791+
IS_ALLOW_MODULE_WITH_COMPILER_ERRORS_ENABLED
791792
};
792793

793794
using SDKPathLayout = BCRecordLayout<
@@ -821,6 +822,10 @@ namespace options_block {
821822
RESILIENCE_STRATEGY,
822823
BCFixed<2>
823824
>;
825+
826+
using IsAllowModuleWithCompilerErrorsEnabledLayout = BCRecordLayout<
827+
IS_ALLOW_MODULE_WITH_COMPILER_ERRORS_ENABLED
828+
>;
824829
}
825830

826831
/// The record types within the input block.
@@ -919,6 +924,12 @@ namespace decls_block {
919924
BCArray<BCVBR<6>>
920925
>;
921926

927+
/// A placeholder for invalid types
928+
using ErrorTypeLayout = BCRecordLayout<
929+
ERROR_TYPE,
930+
TypeIDField // original type (if any)
931+
>;
932+
922933
using BuiltinAliasTypeLayout = BCRecordLayout<
923934
BUILTIN_ALIAS_TYPE,
924935
DeclIDField, // typealias decl

0 commit comments

Comments
 (0)