Skip to content

Commit 5d5e612

Browse files
committed
[GSB] Add verification of all generic signatures within a module.
Add a verification pass to ensure that all of the generic signatures in a module are both minimal and canonical. The approach taken is quite direct: for every (canonical) generic signature in the module, try removing a single requirement and forming a new generic signature from the result. If that new generic signature that provide the removed requirement, then the original signature was not minimal. Also canonicalize each resulting signature, to ensure that it meets the requirements for a canonical signature. Add a test to ensure that all of the generic signatures in the Swift module are minimal and canonical, since they are ABI.
1 parent b1aa23a commit 5d5e612

File tree

7 files changed

+111
-1
lines changed

7 files changed

+111
-1
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,16 @@ class GenericSignatureBuilder {
802802
/// Determine whether the two given types are in the same equivalence class.
803803
bool areInSameEquivalenceClass(Type type1, Type type2);
804804

805+
/// Verify the correctness of the given generic signature.
806+
///
807+
/// This routine will test that the given generic signature is both minimal
808+
/// and canonical, emitting errors if it is not.
809+
static void verifyGenericSignature(ASTContext &context,
810+
GenericSignature *sig);
811+
812+
/// Verify all of the generic sigantures in the given module.
813+
static void verifyGenericSignaturesInModule(ModuleDecl *module);
814+
805815
/// \brief Dump all of the requirements, both specified and inferred.
806816
LLVM_ATTRIBUTE_DEPRECATED(
807817
void dump(),

include/swift/Frontend/FrontendOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,9 @@ class FrontendOptions {
296296
/// too complex.
297297
unsigned SolverExpressionTimeThreshold = 0;
298298

299+
/// The module for which we should verify all of the generic signatures.
300+
std::string VerifyGenericSignaturesInModule;
301+
299302
enum ActionType {
300303
NoneAction, ///< No specific action
301304
Parse, ///< Parse only

include/swift/Option/FrontendOptions.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ def verify_apply_fixes : Flag<["-"], "verify-apply-fixes">,
7676
HelpText<"Like -verify, but updates the original source file">;
7777
def verify_ignore_unknown: Flag<["-"], "verify-ignore-unknown">,
7878
HelpText<"Allow diagnostics for '<unknown>' location in verify mode">;
79+
def verify_generic_signatures : Separate<["-"], "verify-generic-signatures">,
80+
MetaVarName<"<module-name>">,
81+
HelpText<"Verify the generic signatures in the given module">;
7982

8083
def show_diagnostics_after_fatal : Flag<["-"], "show-diagnostics-after-fatal">,
8184
HelpText<"Keep emitting subsequent diagnostics after a fatal error">;

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "swift/AST/LazyResolver.h"
2626
#include "swift/AST/Module.h"
2727
#include "swift/AST/ParameterList.h"
28+
#include "swift/AST/PrettyStackTrace.h"
2829
#include "swift/AST/ProtocolConformance.h"
2930
#include "swift/AST/TypeMatcher.h"
3031
#include "swift/AST/TypeRepr.h"
@@ -6364,7 +6365,7 @@ GenericSignature *GenericSignatureBuilder::computeGenericSignature(
63646365
// over-minimizing.
63656366
if (allowBuilderToMove && !Impl->HadAnyError &&
63666367
!Impl->HadAnyRedundantConstraints) {
6367-
// Register this generic signature builer as the canonical builder for the
6368+
// Register this generic signature builder as the canonical builder for the
63686369
// given signature.
63696370
Context.registerGenericSignatureBuilder(sig, std::move(*this));
63706371
}
@@ -6402,3 +6403,77 @@ GenericSignature *GenericSignatureBuilder::computeRequirementSignature(
64026403
/*allowBuilderToMove=*/false);
64036404
}
64046405

6406+
#pragma mark Generic signature verification
6407+
6408+
void GenericSignatureBuilder::verifyGenericSignature(ASTContext &context,
6409+
GenericSignature *sig) {
6410+
llvm::errs() << "Validating generic signature: ";
6411+
sig->print(llvm::errs());
6412+
llvm::errs() << "\n";
6413+
6414+
// Try removing each requirement in turn.
6415+
auto genericParams = sig->getGenericParams();
6416+
auto requirements = sig->getRequirements();
6417+
for (unsigned victimIndex : indices(requirements)) {
6418+
PrettyStackTraceGenericSignature debugStack("verifying", sig, victimIndex);
6419+
6420+
// Form a new generic signature builder.
6421+
GenericSignatureBuilder builder(context);
6422+
6423+
// Add the generic parameters.
6424+
for (auto gp : genericParams)
6425+
builder.addGenericParameter(gp);
6426+
6427+
// Add the requirements *except* the victim.
6428+
auto source = FloatingRequirementSource::forAbstract();
6429+
for (unsigned i : indices(requirements)) {
6430+
if (i != victimIndex)
6431+
builder.addRequirement(requirements[i], source, nullptr);
6432+
}
6433+
6434+
// Finalize the generic signature. If there were any errors, we formed
6435+
// an invalid signature, so just continue.
6436+
if (builder.Impl->HadAnyError) continue;
6437+
6438+
// Form a generic signature from the result.
6439+
auto newSig =
6440+
std::move(builder).computeGenericSignature(
6441+
SourceLoc(),
6442+
/*allowConcreteGenericParams=*/true,
6443+
/*allowBuilderToMove=*/true);
6444+
6445+
// Check whether the removed requirement
6446+
assert(!newSig->isRequirementSatisfied(requirements[victimIndex]) &&
6447+
"Generic signature is not minimal");
6448+
6449+
// Canonicalize the signature to check that it is canonical.
6450+
(void)newSig->getCanonicalSignature();
6451+
}
6452+
}
6453+
6454+
void GenericSignatureBuilder::verifyGenericSignaturesInModule(
6455+
ModuleDecl *module) {
6456+
LoadedFile *loadedFile = nullptr;
6457+
for (auto fileUnit : module->getFiles()) {
6458+
loadedFile = dyn_cast<LoadedFile>(fileUnit);
6459+
if (loadedFile) break;
6460+
}
6461+
6462+
if (!loadedFile) return;
6463+
6464+
// Check all of the (canonical) generic signatures.
6465+
SmallVector<GenericSignature *, 8> allGenericSignatures;
6466+
SmallPtrSet<CanGenericSignature, 4> knownGenericSignatures;
6467+
(void)loadedFile->getAllGenericSignatures(allGenericSignatures);
6468+
ASTContext &context = module->getASTContext();
6469+
for (auto genericSig : allGenericSignatures) {
6470+
// Check whether this is the first time we've checked this (canonical)
6471+
// signature.
6472+
auto canGenericSig = genericSig->getCanonicalSignature();
6473+
if (!knownGenericSignatures.insert(canGenericSig).second) continue;
6474+
6475+
verifyGenericSignature(context, canGenericSig);
6476+
}
6477+
}
6478+
6479+

lib/Frontend/CompilerInvocation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
229229

230230
Opts.ParseStdlib |= Args.hasArg(OPT_parse_stdlib);
231231

232+
if (const Arg *A = Args.getLastArg(OPT_verify_generic_signatures)) {
233+
Opts.VerifyGenericSignaturesInModule = A->getValue();
234+
}
235+
232236
// Determine what the user has asked the frontend to do.
233237
FrontendOptions::ActionType &Action = Opts.RequestedAction;
234238
if (const Arg *A = Args.getLastArg(OPT_modes_Group)) {

lib/FrontendTool/FrontendTool.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "swift/AST/ASTScope.h"
3131
#include "swift/AST/DiagnosticsFrontend.h"
3232
#include "swift/AST/DiagnosticsSema.h"
33+
#include "swift/AST/GenericSignatureBuilder.h"
3334
#include "swift/AST/IRGenOptions.h"
3435
#include "swift/AST/ASTMangler.h"
3536
#include "swift/AST/LegacyASTTransformer.h"
@@ -630,6 +631,14 @@ static bool performCompile(CompilerInstance &Instance,
630631

631632
ASTContext &Context = Instance.getASTContext();
632633

634+
635+
auto verifyGenericSignaturesInModule =
636+
Invocation.getFrontendOptions().VerifyGenericSignaturesInModule;
637+
if (!verifyGenericSignaturesInModule.empty()) {
638+
if (auto module = Context.getModuleByName(verifyGenericSignaturesInModule))
639+
GenericSignatureBuilder::verifyGenericSignaturesInModule(module);
640+
}
641+
633642
if (Invocation.getMigratorOptions().shouldRunMigrator()) {
634643
migrator::updateCodeAndEmitRemap(&Instance, Invocation);
635644
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Verifies that all of the generic signatures in the standard library are
2+
// minimal and canonical.
3+
4+
// RUN: %target-typecheck-verify-swift -typecheck %s -verify-generic-signatures Swift
5+
6+
// expected-no-diagnostics

0 commit comments

Comments
 (0)