diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 58d3fdc8743..249a227bb7e 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -322,6 +322,9 @@ namespace swift { /// `@differentiable` declaration attribute, etc. bool EnableExperimentalDifferentiableProgramming = false; + /// Enable verification when every SubstitutionMap is constructed. + bool VerifyAllSubstitutionMaps = false; + /// Sets the target we are building for and updates platform conditions /// to match. /// @@ -512,6 +515,7 @@ namespace swift { /// Enable constraint solver support for experimental /// operator protocol designator feature. bool SolverEnableOperatorDesignatedTypes = false; + }; } // end namespace swift diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index f82204d3521..864f001ed38 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -548,6 +548,9 @@ def sil_merge_partial_modules : Flag<["-"], "sil-merge-partial-modules">, def sil_verify_all : Flag<["-"], "sil-verify-all">, HelpText<"Verify SIL after each transform">; + +def verify_all_substitution_maps : Flag<["-"], "verify-all-substitution-maps">, + HelpText<"Verify all SubstitutionMaps on construction">; def sil_debug_serialization : Flag<["-"], "sil-debug-serialization">, HelpText<"Do not eliminate functions in Mandatory Inlining/SILCombine dead " diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index f6300b56ddc..a02ac4d0999 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -3679,8 +3679,26 @@ namespace { void visitSILFunctionType(SILFunctionType *T, StringRef label) { printCommon(label, "sil_function_type"); - // FIXME: Print the structure of the type. printField("type", T->getString()); + + for (auto param : T->getParameters()) { + printRec("input", param.getInterfaceType()); + } + for (auto yield : T->getYields()) { + printRec("yield", yield.getInterfaceType()); + } + for (auto result : T->getResults()) { + printRec("result", result.getInterfaceType()); + } + if (auto error = T->getOptionalErrorResult()) { + printRec("error", error->getInterfaceType()); + } + OS << '\n'; + T->getPatternSubstitutions().dump(OS, SubstitutionMap::DumpStyle::Full, + Indent+2); + OS << '\n'; + T->getInvocationSubstitutions().dump(OS, SubstitutionMap::DumpStyle::Full, + Indent+2); PrintWithColorRAII(OS, ParenthesisColor) << ')'; } diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index dc90a47f771..026111141f8 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -56,7 +56,12 @@ SubstitutionMap::SubstitutionMap( GenericSignature genericSig, ArrayRef replacementTypes, ArrayRef conformances) - : storage(Storage::get(genericSig, replacementTypes, conformances)) { } + : storage(Storage::get(genericSig, replacementTypes, conformances)) { +#ifndef NDEBUG + if (genericSig->getASTContext().LangOpts.VerifyAllSubstitutionMaps) + verify(); +#endif +} ArrayRef SubstitutionMap::getReplacementTypesBuffer() const { return storage ? storage->getReplacementTypes() : ArrayRef(); @@ -355,8 +360,10 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { // Check whether the superclass conforms. if (auto superclass = genericSig->getSuperclassBound(type)) { LookUpConformanceInSignature lookup(getGenericSignature().getPointer()); - if (auto conformance = lookup(type->getCanonicalType(), superclass, proto)) + auto substType = type.subst(*this); + if (auto conformance = lookup(type->getCanonicalType(), substType, proto)){ return conformance; + } } // If the type doesn't conform to this protocol, the result isn't formed @@ -474,7 +481,7 @@ SubstitutionMap SubstitutionMap::subst(TypeSubstitutionFn subs, newConformances.push_back( conformance.subst(substType, subs, conformances, options)); } - + oldConformances = oldConformances.slice(1); } @@ -657,6 +664,7 @@ void SubstitutionMap::verify() const { if (req.getKind() != RequirementKind::Conformance) continue; + SWIFT_DEFER { ++conformanceIndex; }; auto substType = req.getFirstType().subst(*this); if (substType->isTypeParameter() || substType->is() || @@ -677,15 +685,44 @@ void SubstitutionMap::verify() const { llvm::dbgs() << "SubstitutionMap:\n"; dump(llvm::dbgs()); llvm::dbgs() << "\n"; + llvm::dbgs() << "Requirement:\n"; + req.dump(llvm::dbgs()); + llvm::dbgs() << "\n"; } assert(conformance.isConcrete() && "Conformance should be concrete"); + + if (substType->is()) + continue; + + auto conformanceTy = conformance.getConcrete()->getType(); + if (conformanceTy->hasTypeParameter() + && !substType->hasTypeParameter()) { + conformanceTy = conformance.getConcrete()->getDeclContext() + ->mapTypeIntoContext(conformanceTy); + } + + if (!substType->isEqual(conformanceTy)) { + llvm::dbgs() << "Conformance must match concrete replacement type:\n"; + substType->dump(llvm::dbgs()); + llvm::dbgs() << "Conformance type:\n"; + conformance.getConcrete()->getType()->dump(llvm::dbgs()); + llvm::dbgs() << "Conformance:\n"; + conformance.dump(llvm::dbgs()); + llvm::dbgs() << "\n"; + llvm::dbgs() << "SubstitutionMap:\n"; + dump(llvm::dbgs()); + llvm::dbgs() << "\n"; + llvm::dbgs() << "Requirement:\n"; + req.dump(llvm::dbgs()); + llvm::dbgs() << "\n"; + } + assert(substType->isEqual(conformanceTy) + && "conformance should match corresponding type"); if (substType->isExistentialType()) { assert(isa(conformance.getConcrete()) && "Existential type cannot have normal conformance"); } - - ++conformanceIndex; } #endif } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index d0da20183ee..988d6fea07a 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2400,7 +2400,7 @@ getForeignRepresentable(Type type, ForeignLanguage language, if (wasOptional && !result.isRepresentableAsOptional()) return failure(); - + // If our nominal type has type arguments, make sure they are // representable as well. Because type arguments are not actually // translated separately, whether they are trivially representable @@ -2447,6 +2447,16 @@ getForeignRepresentable(Type type, ForeignLanguage language, break; } } + + // Specialize the conformance we were given for the type we're testing. + if (result.getKind() == ForeignRepresentableKind::Bridged + && !result.getConformance()->getType()->isEqual(type)) { + auto specialized = type->getASTContext() + .getSpecializedConformance(type, result.getConformance(), + boundGenericType->getContextSubstitutionMap(dc->getParentModule(), + boundGenericType->getDecl())); + result = ForeignRepresentationInfo::forBridged(specialized); + } } return { result.getKind(), result.getConformance() }; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 3ea35141fb5..12db9b35232 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -565,6 +565,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Target.isOSDarwin()); Opts.EnableSILOpaqueValues |= Args.hasArg(OPT_enable_sil_opaque_values); + Opts.VerifyAllSubstitutionMaps |= Args.hasArg(OPT_verify_all_substitution_maps); + Opts.UseDarwinPreStableABIBit = (Target.isMacOSX() && Target.isMacOSXVersionLT(10, 14, 4)) || (Target.isiOS() && Target.isOSVersionLT(12, 2)) || @@ -795,7 +797,7 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts, // Assumes exactly one of setMainExecutablePath() or setRuntimeIncludePath() // is called before setTargetTriple() and parseArgs(). // TODO: improve the handling of RuntimeIncludePath. - + return false; } diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index e4ff2fc528c..23ee52279a4 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -749,7 +749,7 @@ static SILFunction *emitSelfConformanceWitness(SILGenModule &SGM, auto protocolType = protocol->getDeclaredInterfaceType(); auto reqtSubs = SubstitutionMap::getProtocolSubstitutions(protocol, protocolType, - ProtocolConformanceRef(protocol)); + ProtocolConformanceRef(conformance)); // Open the protocol type. auto openedType = OpenedArchetypeType::get(protocolType); diff --git a/test/SILGen/function_type_lowering_inherited_conformance.swift b/test/SILGen/function_type_lowering_inherited_conformance.swift new file mode 100644 index 00000000000..5a7721270d0 --- /dev/null +++ b/test/SILGen/function_type_lowering_inherited_conformance.swift @@ -0,0 +1,15 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s +protocol P {} + +class C: P {} +class D: C {} + +struct Butt {} + +func foo(_: (Butt) -> ()) {} + +// CHECK-LABEL: sil{{.*}}3bar +func bar(_ f: (Butt) -> ()) { + // CHECK: convert_function {{.*}} $@noescape @callee_guaranteed (Butt) -> () to $@noescape @callee_guaranteed @substituted <τ_0_0 where τ_0_0 : P> (Butt<τ_0_0>) -> () for + foo(f) +}