Skip to content

Commit

Permalink
Merge pull request #12805 from DougGregor/validate-stdlib-generic-sig…
Browse files Browse the repository at this point in the history
…natures
  • Loading branch information
swift-ci authored Nov 8, 2017
2 parents d99c72a + 5d5e612 commit 8614620
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 1 deletion.
10 changes: 10 additions & 0 deletions include/swift/AST/GenericSignatureBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,16 @@ class GenericSignatureBuilder {
/// Determine whether the two given types are in the same equivalence class.
bool areInSameEquivalenceClass(Type type1, Type type2);

/// Verify the correctness of the given generic signature.
///
/// This routine will test that the given generic signature is both minimal
/// and canonical, emitting errors if it is not.
static void verifyGenericSignature(ASTContext &context,
GenericSignature *sig);

/// Verify all of the generic sigantures in the given module.
static void verifyGenericSignaturesInModule(ModuleDecl *module);

/// \brief Dump all of the requirements, both specified and inferred.
LLVM_ATTRIBUTE_DEPRECATED(
void dump(),
Expand Down
9 changes: 9 additions & 0 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,15 @@ class LoadedFile : public FileUnit {

virtual bool isSystemModule() const { return false; }

/// Retrieve the set of generic signatures stored within this module.
///
/// \returns \c true if this module file supports retrieving all of the
/// generic signatures, \c false otherwise.
virtual bool getAllGenericSignatures(
SmallVectorImpl<GenericSignature*> &genericSignatures) {
return false;
}

static bool classof(const FileUnit *file) {
return file->getKind() == FileUnitKind::SerializedAST ||
file->getKind() == FileUnitKind::ClangModule;
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Frontend/FrontendOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,9 @@ class FrontendOptions {
/// too complex.
unsigned SolverExpressionTimeThreshold = 0;

/// The module for which we should verify all of the generic signatures.
std::string VerifyGenericSignaturesInModule;

enum ActionType {
NoneAction, ///< No specific action
Parse, ///< Parse only
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ def verify_apply_fixes : Flag<["-"], "verify-apply-fixes">,
HelpText<"Like -verify, but updates the original source file">;
def verify_ignore_unknown: Flag<["-"], "verify-ignore-unknown">,
HelpText<"Allow diagnostics for '<unknown>' location in verify mode">;
def verify_generic_signatures : Separate<["-"], "verify-generic-signatures">,
MetaVarName<"<module-name>">,
HelpText<"Verify the generic signatures in the given module">;

def show_diagnostics_after_fatal : Flag<["-"], "show-diagnostics-after-fatal">,
HelpText<"Keep emitting subsequent diagnostics after a fatal error">;
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Serialization/SerializedModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ class SerializedASTFile final : public LoadedFile {

virtual const clang::Module *getUnderlyingClangModule() const override;

virtual bool getAllGenericSignatures(
SmallVectorImpl<GenericSignature*> &genericSignatures)
override;

static bool classof(const FileUnit *file) {
return file->getKind() == FileUnitKind::SerializedAST;
}
Expand Down
77 changes: 76 additions & 1 deletion lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "swift/AST/LazyResolver.h"
#include "swift/AST/Module.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/TypeMatcher.h"
#include "swift/AST/TypeRepr.h"
Expand Down Expand Up @@ -6364,7 +6365,7 @@ GenericSignature *GenericSignatureBuilder::computeGenericSignature(
// over-minimizing.
if (allowBuilderToMove && !Impl->HadAnyError &&
!Impl->HadAnyRedundantConstraints) {
// Register this generic signature builer as the canonical builder for the
// Register this generic signature builder as the canonical builder for the
// given signature.
Context.registerGenericSignatureBuilder(sig, std::move(*this));
}
Expand Down Expand Up @@ -6402,3 +6403,77 @@ GenericSignature *GenericSignatureBuilder::computeRequirementSignature(
/*allowBuilderToMove=*/false);
}

#pragma mark Generic signature verification

void GenericSignatureBuilder::verifyGenericSignature(ASTContext &context,
GenericSignature *sig) {
llvm::errs() << "Validating generic signature: ";
sig->print(llvm::errs());
llvm::errs() << "\n";

// Try removing each requirement in turn.
auto genericParams = sig->getGenericParams();
auto requirements = sig->getRequirements();
for (unsigned victimIndex : indices(requirements)) {
PrettyStackTraceGenericSignature debugStack("verifying", sig, victimIndex);

// Form a new generic signature builder.
GenericSignatureBuilder builder(context);

// Add the generic parameters.
for (auto gp : genericParams)
builder.addGenericParameter(gp);

// Add the requirements *except* the victim.
auto source = FloatingRequirementSource::forAbstract();
for (unsigned i : indices(requirements)) {
if (i != victimIndex)
builder.addRequirement(requirements[i], source, nullptr);
}

// Finalize the generic signature. If there were any errors, we formed
// an invalid signature, so just continue.
if (builder.Impl->HadAnyError) continue;

// Form a generic signature from the result.
auto newSig =
std::move(builder).computeGenericSignature(
SourceLoc(),
/*allowConcreteGenericParams=*/true,
/*allowBuilderToMove=*/true);

// Check whether the removed requirement
assert(!newSig->isRequirementSatisfied(requirements[victimIndex]) &&
"Generic signature is not minimal");

// Canonicalize the signature to check that it is canonical.
(void)newSig->getCanonicalSignature();
}
}

void GenericSignatureBuilder::verifyGenericSignaturesInModule(
ModuleDecl *module) {
LoadedFile *loadedFile = nullptr;
for (auto fileUnit : module->getFiles()) {
loadedFile = dyn_cast<LoadedFile>(fileUnit);
if (loadedFile) break;
}

if (!loadedFile) return;

// Check all of the (canonical) generic signatures.
SmallVector<GenericSignature *, 8> allGenericSignatures;
SmallPtrSet<CanGenericSignature, 4> knownGenericSignatures;
(void)loadedFile->getAllGenericSignatures(allGenericSignatures);
ASTContext &context = module->getASTContext();
for (auto genericSig : allGenericSignatures) {
// Check whether this is the first time we've checked this (canonical)
// signature.
auto canGenericSig = genericSig->getCanonicalSignature();
if (!knownGenericSignatures.insert(canGenericSig).second) continue;

verifyGenericSignature(context, canGenericSig);
}
}


4 changes: 4 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,

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

if (const Arg *A = Args.getLastArg(OPT_verify_generic_signatures)) {
Opts.VerifyGenericSignaturesInModule = A->getValue();
}

// Determine what the user has asked the frontend to do.
FrontendOptions::ActionType &Action = Opts.RequestedAction;
if (const Arg *A = Args.getLastArg(OPT_modes_Group)) {
Expand Down
9 changes: 9 additions & 0 deletions lib/FrontendTool/FrontendTool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "swift/AST/ASTScope.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/GenericSignatureBuilder.h"
#include "swift/AST/IRGenOptions.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/LegacyASTTransformer.h"
Expand Down Expand Up @@ -630,6 +631,14 @@ static bool performCompile(CompilerInstance &Instance,

ASTContext &Context = Instance.getASTContext();


auto verifyGenericSignaturesInModule =
Invocation.getFrontendOptions().VerifyGenericSignaturesInModule;
if (!verifyGenericSignaturesInModule.empty()) {
if (auto module = Context.getModuleByName(verifyGenericSignaturesInModule))
GenericSignatureBuilder::verifyGenericSignaturesInModule(module);
}

if (Invocation.getMigratorOptions().shouldRunMigrator()) {
migrator::updateCodeAndEmitRemap(&Instance, Invocation);
}
Expand Down
11 changes: 11 additions & 0 deletions lib/Serialization/ModuleFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2175,6 +2175,17 @@ bool SerializedASTFile::hasEntryPoint() const {
return File.Bits.HasEntryPoint;
}

bool SerializedASTFile::getAllGenericSignatures(
SmallVectorImpl<GenericSignature*> &genericSignatures) {
genericSignatures.clear();
for (unsigned index : indices(File.GenericSignatures)) {
if (auto genericSig = File.getGenericSignature(index + 1))
genericSignatures.push_back(genericSig);
}

return true;
}

ClassDecl *SerializedASTFile::getMainClass() const {
assert(hasEntryPoint());
return cast_or_null<ClassDecl>(File.getDecl(File.Bits.EntryPointDeclID));
Expand Down
6 changes: 6 additions & 0 deletions test/Generics/validate_stdlib_generic_signatures.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Verifies that all of the generic signatures in the standard library are
// minimal and canonical.

// RUN: %target-typecheck-verify-swift -typecheck %s -verify-generic-signatures Swift

// expected-no-diagnostics

0 comments on commit 8614620

Please sign in to comment.