From 720d3d2f2adb8691e800a1dcd6293a51d6597782 Mon Sep 17 00:00:00 2001 From: Ashley Garland Date: Tue, 19 May 2020 15:03:38 -0700 Subject: [PATCH 01/12] [SymbolGraph] Show get/set on property/subscript full declarations But don't show them in subheading contexts. rdar://63233897 --- lib/SymbolGraphGen/SymbolGraph.cpp | 16 +- ...DeclarationFragments.swift => Basic.swift} | 6 +- .../Properties/ComputedProperties.swift | 162 ++++++++++++++++++ .../Properties/ProtocolRequirements.swift | 82 +++++++++ .../Properties/Subscripts.swift | 95 ++++++++++ 5 files changed, 355 insertions(+), 6 deletions(-) rename test/SymbolGraph/Symbols/Mixins/DeclarationFragments/{DeclarationFragments.swift => Basic.swift} (81%) create mode 100644 test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Properties/ComputedProperties.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Properties/ProtocolRequirements.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Properties/Subscripts.swift diff --git a/lib/SymbolGraphGen/SymbolGraph.cpp b/lib/SymbolGraphGen/SymbolGraph.cpp index fb60ff321df7c..29ade8e54f166 100644 --- a/lib/SymbolGraphGen/SymbolGraph.cpp +++ b/lib/SymbolGraphGen/SymbolGraph.cpp @@ -48,9 +48,9 @@ PrintOptions SymbolGraph::getDeclarationFragmentsPrintOptions() const { Opts.FunctionDefinitions = false; Opts.ArgAndParamPrinting = PrintOptions::ArgAndParamPrintingMode::MatchSource; - Opts.PrintGetSetOnRWProperties = false; - Opts.PrintPropertyAccessors = false; - Opts.PrintSubscriptAccessors = false; + Opts.PrintGetSetOnRWProperties = true; + Opts.PrintPropertyAccessors = true; + Opts.PrintSubscriptAccessors = true; Opts.SkipUnderscoredKeywords = true; Opts.SkipAttributes = true; Opts.PrintOverrideKeyword = true; @@ -76,6 +76,16 @@ SymbolGraph::getSubHeadingDeclarationFragmentsPrintOptions() const { auto Options = getDeclarationFragmentsPrintOptions(); Options.ArgAndParamPrinting = PrintOptions::ArgAndParamPrintingMode::ArgumentOnly; + + //--------------------------------------------------------------------------// + // Although we want these in the full declaration presentation, + // particularly for protocol requirements, + // we don't want to show these in subheadings. + Options.PrintGetSetOnRWProperties = false; + Options.PrintPropertyAccessors = false; + Options.PrintSubscriptAccessors = false; + //--------------------------------------------------------------------------// + Options.VarInitializers = false; Options.PrintDefaultArgumentValue = false; Options.PrintEmptyArgumentNames = false; diff --git a/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/DeclarationFragments.swift b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Basic.swift similarity index 81% rename from test/SymbolGraph/Symbols/Mixins/DeclarationFragments/DeclarationFragments.swift rename to test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Basic.swift index 423f5d73cf92b..2c46cf5c7c853 100644 --- a/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/DeclarationFragments.swift +++ b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Basic.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift %s -module-name DeclarationFragments -emit-module -emit-module-path %t/ -// RUN: %target-swift-symbolgraph-extract -module-name DeclarationFragments -I %t -pretty-print -output-dir %t -// RUN: %FileCheck %s --input-file %t/DeclarationFragments.symbols.json +// RUN: %target-build-swift %s -module-name Basic -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Basic -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/Basic.symbols.json public func foo(f: @escaping () -> (), ext int: Int = 2, s: S) {} diff --git a/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Properties/ComputedProperties.swift b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Properties/ComputedProperties.swift new file mode 100644 index 0000000000000..250bbb694268a --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Properties/ComputedProperties.swift @@ -0,0 +1,162 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name ComputedProperties -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name ComputedProperties -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/ComputedProperties.symbols.json --check-prefix=XFULL +// RUN: %FileCheck %s --input-file %t/ComputedProperties.symbols.json --check-prefix=XSUBHEADING +// RUN: %FileCheck %s --input-file %t/ComputedProperties.symbols.json --check-prefix=YFULL +// RUN: %FileCheck %s --input-file %t/ComputedProperties.symbols.json --check-prefix=YSUBHEADING + +public struct S { + // We should show { get set } here for the + // full declaration, but not for subheadings. + public var x: Int { + get { return 7 } + set {} + } + public private(set) var y: Int { + get { return 7 } + set {} + } +} + +// XFULL-LABEL: "precise": "s:18ComputedProperties1SV1xSivp" +// XFULL: "declarationFragments": [ +// XFULL-NEXT: { +// XFULL-NEXT: "kind": "keyword", +// XFULL-NEXT: "spelling": "var" +// XFULL-NEXT: }, +// XFULL-NEXT: { +// XFULL-NEXT: "kind": "text", +// XFULL-NEXT: "spelling": " " +// XFULL-NEXT: }, +// XFULL-NEXT: { +// XFULL-NEXT: "kind": "identifier", +// XFULL-NEXT: "spelling": "x" +// XFULL-NEXT: }, +// XFULL-NEXT: { +// XFULL-NEXT: "kind": "text", +// XFULL-NEXT: "spelling": ": " +// XFULL-NEXT: }, +// XFULL-NEXT: { +// XFULL-NEXT: "kind": "typeIdentifier", +// XFULL-NEXT: "spelling": "Int", +// XFULL-NEXT: "preciseIdentifier": "s:Si" +// XFULL-NEXT: }, +// XFULL-NEXT: { +// XFULL-NEXT: "kind": "text", +// XFULL-NEXT: "spelling": " { " +// XFULL-NEXT: }, +// XFULL-NEXT: { +// XFULL-NEXT: "kind": "keyword", +// XFULL-NEXT: "spelling": "get" +// XFULL-NEXT: }, +// XFULL-NEXT: { +// XFULL-NEXT: "kind": "text", +// XFULL-NEXT: "spelling": " " +// XFULL-NEXT: }, +// XFULL-NEXT: { +// XFULL-NEXT: "kind": "keyword", +// XFULL-NEXT: "spelling": "set" +// XFULL-NEXT: }, +// XFULL-NEXT: { +// XFULL-NEXT: "kind": "text", +// XFULL-NEXT: "spelling": " }" +// XFULL-NEXT: } +// XFULL-NEXT: ], + +// XSUBHEADING-LABEL: "precise": "s:18ComputedProperties1SV1xSivp" +// XSUBHEADING: names +// XSUBHEADING: "subHeading": [ +// XSUBHEADING-NEXT: { +// XSUBHEADING-NEXT: "kind": "keyword", +// XSUBHEADING-NEXT: "spelling": "var" +// XSUBHEADING-NEXT: }, +// XSUBHEADING-NEXT: { +// XSUBHEADING-NEXT: "kind": "text", +// XSUBHEADING-NEXT: "spelling": " " +// XSUBHEADING-NEXT: }, +// XSUBHEADING-NEXT: { +// XSUBHEADING-NEXT: "kind": "identifier", +// XSUBHEADING-NEXT: "spelling": "x" +// XSUBHEADING-NEXT: }, +// XSUBHEADING-NEXT: { +// XSUBHEADING-NEXT: "kind": "text", +// XSUBHEADING-NEXT: "spelling": ": " +// XSUBHEADING-NEXT: }, +// XSUBHEADING-NEXT: { +// XSUBHEADING-NEXT: "kind": "typeIdentifier", +// XSUBHEADING-NEXT: "spelling": "Int", +// XSUBHEADING-NEXT: "preciseIdentifier": "s:Si" +// XSUBHEADING-NEXT: } +// XSUBHEADING-NEXT: ] + +// YFULL-LABEL: "precise": "s:18ComputedProperties1SV1ySivp" +// YFULL: "declarationFragments": [ +// YFULL-NEXT: { +// YFULL-NEXT: "kind": "keyword", +// YFULL-NEXT: "spelling": "var" +// YFULL-NEXT: }, +// YFULL-NEXT: { +// YFULL-NEXT: "kind": "text", +// YFULL-NEXT: "spelling": " " +// YFULL-NEXT: }, +// YFULL-NEXT: { +// YFULL-NEXT: "kind": "identifier", +// YFULL-NEXT: "spelling": "y" +// YFULL-NEXT: }, +// YFULL-NEXT: { +// YFULL-NEXT: "kind": "text", +// YFULL-NEXT: "spelling": ": " +// YFULL-NEXT: }, +// YFULL-NEXT: { +// YFULL-NEXT: "kind": "typeIdentifier", +// YFULL-NEXT: "spelling": "Int", +// YFULL-NEXT: "preciseIdentifier": "s:Si" +// YFULL-NEXT: }, +// YFULL-NEXT: { +// YFULL-NEXT: "kind": "text", +// YFULL-NEXT: "spelling": " { " +// YFULL-NEXT: }, +// YFULL-NEXT: { +// YFULL-NEXT: "kind": "keyword", +// YFULL-NEXT: "spelling": "get" +// YFULL-NEXT: }, +// YFULL-NEXT: { +// YFULL-NEXT: "kind": "text", +// YFULL-NEXT: "spelling": " " +// YFULL-NEXT: }, +// YFULL-NEXT: { +// YFULL-NEXT: "kind": "keyword", +// YFULL-NEXT: "spelling": "set" +// YFULL-NEXT: }, +// YFULL-NEXT: { +// YFULL-NEXT: "kind": "text", +// YFULL-NEXT: "spelling": " }" +// YFULL-NEXT: } +// YFULL-NEXT: ] + +// YSUBHEADING-LABEL: "precise": "s:18ComputedProperties1SV1ySivp" +// YSUBHEADING: names +// YSUBHEADING: "subHeading": [ +// YSUBHEADING-NEXT: { +// YSUBHEADING-NEXT: "kind": "keyword", +// YSUBHEADING-NEXT: "spelling": "var" +// YSUBHEADING-NEXT: }, +// YSUBHEADING-NEXT: { +// YSUBHEADING-NEXT: "kind": "text", +// YSUBHEADING-NEXT: "spelling": " " +// YSUBHEADING-NEXT: }, +// YSUBHEADING-NEXT: { +// YSUBHEADING-NEXT: "kind": "identifier", +// YSUBHEADING-NEXT: "spelling": "y" +// YSUBHEADING-NEXT: }, +// YSUBHEADING-NEXT: { +// YSUBHEADING-NEXT: "kind": "text", +// YSUBHEADING-NEXT: "spelling": ": " +// YSUBHEADING-NEXT: }, +// YSUBHEADING-NEXT: { +// YSUBHEADING-NEXT: "kind": "typeIdentifier", +// YSUBHEADING-NEXT: "spelling": "Int", +// YSUBHEADING-NEXT: "preciseIdentifier": "s:Si" +// YSUBHEADING-NEXT: } +// YSUBHEADING-NEXT: ] diff --git a/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Properties/ProtocolRequirements.swift b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Properties/ProtocolRequirements.swift new file mode 100644 index 0000000000000..d4e74ea184184 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Properties/ProtocolRequirements.swift @@ -0,0 +1,82 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name ProtocolRequirements -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name ProtocolRequirements -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/ProtocolRequirements.symbols.json --check-prefix=FULL +// RUN: %FileCheck %s --input-file %t/ProtocolRequirements.symbols.json --check-prefix=SUBHEADING + +public protocol P { + // We should show { get set } here for the + // full declaration, but not for subheadings. + var x: Int { get set } +} + +// FULL-LABEL: "precise": "s:20ProtocolRequirements1PP1xSivp" +// FULL: "declarationFragments": [ +// FULL-NEXT: { +// FULL-NEXT: "kind": "keyword", +// FULL-NEXT: "spelling": "var" +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "text", +// FULL-NEXT: "spelling": " " +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "identifier", +// FULL-NEXT: "spelling": "x" +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "text", +// FULL-NEXT: "spelling": ": " +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "typeIdentifier", +// FULL-NEXT: "spelling": "Int", +// FULL-NEXT: "preciseIdentifier": "s:Si" +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "text", +// FULL-NEXT: "spelling": " { " +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "keyword", +// FULL-NEXT: "spelling": "get" +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "text", +// FULL-NEXT: "spelling": " " +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "keyword", +// FULL-NEXT: "spelling": "set" +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "text", +// FULL-NEXT: "spelling": " }" +// FULL-NEXT: } +// FULL-NEXT: ] + +// SUBHEADING-LABEL: "precise": "s:20ProtocolRequirements1PP1xSivp" +// SUBHEADING: names +// SUBHEADING: "subHeading": [ +// SUBHEADING-NEXT: { +// SUBHEADING-NEXT: "kind": "keyword", +// SUBHEADING-NEXT: "spelling": "var" +// SUBHEADING-NEXT: }, +// SUBHEADING-NEXT: { +// SUBHEADING-NEXT: "kind": "text", +// SUBHEADING-NEXT: "spelling": " " +// SUBHEADING-NEXT: }, +// SUBHEADING-NEXT: { +// SUBHEADING-NEXT: "kind": "identifier", +// SUBHEADING-NEXT: "spelling": "x" +// SUBHEADING-NEXT: }, +// SUBHEADING-NEXT: { +// SUBHEADING-NEXT: "kind": "text", +// SUBHEADING-NEXT: "spelling": ": " +// SUBHEADING-NEXT: }, +// SUBHEADING-NEXT: { +// SUBHEADING-NEXT: "kind": "typeIdentifier", +// SUBHEADING-NEXT: "spelling": "Int", +// SUBHEADING-NEXT: "preciseIdentifier": "s:Si" +// SUBHEADING-NEXT: } +// SUBHEADING-NEXT: ] diff --git a/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Properties/Subscripts.swift b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Properties/Subscripts.swift new file mode 100644 index 0000000000000..6e05712bb16e8 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/Properties/Subscripts.swift @@ -0,0 +1,95 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Subscripts -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Subscripts -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/Subscripts.symbols.json --check-prefix=FULL +// RUN: %FileCheck %s --input-file %t/Subscripts.symbols.json --check-prefix=SUBHEADING + +public struct S { + public subscript(i: Int) -> Int { + get { + return 7 + } + set {} + } +} + +// FULL-LABEL: "precise": "s:10Subscripts1SVyS2icip" +// FULL: "declarationFragments": [ +// FULL-NEXT: { +// FULL-NEXT: "kind": "keyword", +// FULL-NEXT: "spelling": "subscript" +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "text", +// FULL-NEXT: "spelling": "(" +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "internalParam", +// FULL-NEXT: "spelling": "i" +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "text", +// FULL-NEXT: "spelling": ": " +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "typeIdentifier", +// FULL-NEXT: "spelling": "Int", +// FULL-NEXT: "preciseIdentifier": "s:Si" +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "text", +// FULL-NEXT: "spelling": ") -> " +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "typeIdentifier", +// FULL-NEXT: "spelling": "Int", +// FULL-NEXT: "preciseIdentifier": "s:Si" +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "text", +// FULL-NEXT: "spelling": " { " +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "keyword", +// FULL-NEXT: "spelling": "get" +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "text", +// FULL-NEXT: "spelling": " " +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "keyword", +// FULL-NEXT: "spelling": "set" +// FULL-NEXT: }, +// FULL-NEXT: { +// FULL-NEXT: "kind": "text", +// FULL-NEXT: "spelling": " }" +// FULL-NEXT: } +// FULL-NEXT: ] + +// SUBHEADING-LABEL: "precise": "s:10Subscripts1SVyS2icip" +// SUBHEADING: names +// SUBHEADING: "subHeading": [ +// SUBHEADING-NEXT: { +// SUBHEADING-NEXT: "kind": "keyword", +// SUBHEADING-NEXT: "spelling": "subscript" +// SUBHEADING-NEXT: }, +// SUBHEADING-NEXT: { +// SUBHEADING-NEXT: "kind": "text", +// SUBHEADING-NEXT: "spelling": "(" +// SUBHEADING-NEXT: }, +// SUBHEADING-NEXT: { +// SUBHEADING-NEXT: "kind": "typeIdentifier", +// SUBHEADING-NEXT: "spelling": "Int", +// SUBHEADING-NEXT: "preciseIdentifier": "s:Si" +// SUBHEADING-NEXT: }, +// SUBHEADING-NEXT: { +// SUBHEADING-NEXT: "kind": "text", +// SUBHEADING-NEXT: "spelling": ") -> " +// SUBHEADING-NEXT: }, +// SUBHEADING-NEXT: { +// SUBHEADING-NEXT: "kind": "typeIdentifier", +// SUBHEADING-NEXT: "spelling": "Int", +// SUBHEADING-NEXT: "preciseIdentifier": "s:Si" +// SUBHEADING-NEXT: } +// SUBHEADING-NEXT: ] From f13b00fc6def2d5bef9aee851c074c1ba870bafa Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sun, 24 May 2020 10:39:48 -0300 Subject: [PATCH 02/12] [CSFix] Adjusting tuple mismatch to handle optionals --- lib/Sema/CSFix.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index c3875c5500cf9..fb0950e2e4e89 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -289,7 +289,7 @@ getStructuralTypeContext(const Solution &solution, ConstraintLocator *locator) { } else if (auto *assignExpr = getAsExpr(locator->getAnchor())) { return std::make_tuple(CTP_AssignSource, solution.getType(assignExpr->getSrc()), - solution.getType(assignExpr->getDest())); + solution.getType(assignExpr->getDest())->getRValueType()); } else if (auto *call = getAsExpr(locator->getAnchor())) { assert(isa(call->getFn())); return std::make_tuple( @@ -332,8 +332,10 @@ bool AllowTupleTypeMismatch::coalesceAndDiagnose( return false; } - TupleContextualFailure failure(solution, purpose, fromType, toType, indices, - locator); + TupleContextualFailure failure(solution, purpose, + fromType->lookThroughAllOptionalTypes(), + toType->lookThroughAllOptionalTypes(), + indices, locator); return failure.diagnose(asNote); } From c7fdbdf95e1b98799f285af3cab3b1d1fa8dc893 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sun, 24 May 2020 10:51:01 -0300 Subject: [PATCH 03/12] [tests] Adding regression tests for SR-12869 --- test/Constraints/tuple.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/Constraints/tuple.swift b/test/Constraints/tuple.swift index ced92529e35ec..7f647ba595899 100644 --- a/test/Constraints/tuple.swift +++ b/test/Constraints/tuple.swift @@ -318,3 +318,13 @@ struct DupLabelSubscript { let dupLabelSubscriptStruct = DupLabelSubscript() let _ = dupLabelSubscriptStruct[foo: 5, foo: 5] // ok + +// SR-12869 + +var dict: [String: (Int, Int)] = [:] +let bignum: Int64 = 1337 +dict["test"] = (bignum, 1) // expected-error {{cannot assign value of type '(Int64, Int)' to type '(Int, Int)'}} +// expected-error@-1 {{cannot assign value of type '(Int64, Int)' to subscript of type '(Int, Int)'}} + +var tuple: (Int, Int) +tuple = (bignum, 1) // expected-error {{cannot assign value of type '(Int64, Int)' to type '(Int, Int)'}} From ab0e8a8e8e9b2cf70426d6e6263f07fd1c7c9504 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sun, 24 May 2020 16:08:58 -0300 Subject: [PATCH 04/12] [CSSimplify] Do not record IgnoreAssignmentDestinationType or contextual if we already record a tuple mismatch --- lib/Sema/CSSimplify.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index b8b54d33893c5..8a7e6a613dd2d 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3356,13 +3356,19 @@ bool ConstraintSystem::repairFailures( // related to immutability, otherwise it's a type mismatch. auto result = matchTypes(lhs, rhs, ConstraintKind::Conversion, TMF_ApplyingFix, locator); - + + auto *loc = getConstraintLocator(locator); if (getType(destExpr)->is() || result.isFailure()) { - conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create( - *this, lhs, rhs, getConstraintLocator(locator))); + // Let this asignment failure be diagnosed by the AllowTupleTypeMismatch + // fix already recorded. + if (hasFixFor(loc, FixKind::AllowTupleTypeMismatch)) + return true; + + conversionsOrFixes.push_back( + IgnoreAssignmentDestinationType::create(*this, lhs, rhs, loc)); } else { conversionsOrFixes.push_back( - TreatRValueAsLValue::create(*this, getConstraintLocator(locator))); + TreatRValueAsLValue::create(*this, loc)); } return true; @@ -8865,11 +8871,16 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( CoerceToCheckedCast::attempt(*this, fromType, toType, loc)) return !recordFix(fix, impact); } - + + // We already have a fix for this locator indicating a + // tuple mismatch. + if (hasFixFor(loc, FixKind::AllowTupleTypeMismatch)) + return true; + if (restriction == ConversionRestrictionKind::ValueToOptional || restriction == ConversionRestrictionKind::OptionalToOptional) ++impact; - + auto *fix = loc->isLastElement() ? AllowArgumentMismatch::create(*this, fromType, toType, loc) From 086ad339c946b5458999a467559b56566ca7b1be Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sun, 24 May 2020 16:09:12 -0300 Subject: [PATCH 05/12] [CSDiagnostics] Adjust tuple contextual mismatch diagnostics to handle Subscript assign to source --- lib/Sema/CSDiagnostics.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 6900f5bd38989..0bdb4e75e443e 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -2989,7 +2989,14 @@ bool TupleContextualFailure::diagnoseAsError() { diagnostic = diag::tuple_types_not_convertible_nelts; else if ((purpose == CTP_Initialization) && !getContextualType(getAnchor())) diagnostic = diag::tuple_types_not_convertible; - else if (auto diag = getDiagnosticFor(purpose, getToType())) + else if (purpose == CTP_AssignSource) { + auto assignExpr = castToExpr(getAnchor()); + if (isa(assignExpr->getDest())) { + diagnostic = *getDiagnosticFor(CTP_SubscriptAssignSource, getToType()); + } else { + diagnostic = *getDiagnosticFor(purpose, getToType()); + } + } else if (auto diag = getDiagnosticFor(purpose, getToType())) diagnostic = *diag; else return false; From 2e1609e53385fc78b2a9d62dd81c97a918a5601c Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sun, 24 May 2020 16:09:19 -0300 Subject: [PATCH 06/12] [tests] Adjusting SR-12869 test cases for optional mismatches --- lib/Sema/CSSimplify.cpp | 7 +++---- test/Constraints/tuple.swift | 11 +++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 8a7e6a613dd2d..4fd66d5a5cbf3 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3367,8 +3367,7 @@ bool ConstraintSystem::repairFailures( conversionsOrFixes.push_back( IgnoreAssignmentDestinationType::create(*this, lhs, rhs, loc)); } else { - conversionsOrFixes.push_back( - TreatRValueAsLValue::create(*this, loc)); + conversionsOrFixes.push_back(TreatRValueAsLValue::create(*this, loc)); } return true; @@ -8876,11 +8875,11 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( // tuple mismatch. if (hasFixFor(loc, FixKind::AllowTupleTypeMismatch)) return true; - + if (restriction == ConversionRestrictionKind::ValueToOptional || restriction == ConversionRestrictionKind::OptionalToOptional) ++impact; - + auto *fix = loc->isLastElement() ? AllowArgumentMismatch::create(*this, fromType, toType, loc) diff --git a/test/Constraints/tuple.swift b/test/Constraints/tuple.swift index 7f647ba595899..c452ea7b2b335 100644 --- a/test/Constraints/tuple.swift +++ b/test/Constraints/tuple.swift @@ -323,8 +323,15 @@ let _ = dupLabelSubscriptStruct[foo: 5, foo: 5] // ok var dict: [String: (Int, Int)] = [:] let bignum: Int64 = 1337 -dict["test"] = (bignum, 1) // expected-error {{cannot assign value of type '(Int64, Int)' to type '(Int, Int)'}} -// expected-error@-1 {{cannot assign value of type '(Int64, Int)' to subscript of type '(Int, Int)'}} +dict["test"] = (bignum, 1) // expected-error {{cannot assign value of type '(Int64, Int)' to subscript of type '(Int, Int)'}} var tuple: (Int, Int) tuple = (bignum, 1) // expected-error {{cannot assign value of type '(Int64, Int)' to type '(Int, Int)'}} + +var optionalTuple: (Int, Int)? +var optionalTuple2: (Int64, Int)? = (bignum, 1) +var optionalTuple3: (UInt64, Int)? = (bignum, 1) // expected-error {{cannot convert value of type '(Int64, Int)' to specified type '(UInt64, Int)?'}} + +optionalTuple = (bignum, 1) // expected-error {{cannot assign value of type '(Int64, Int)' to type '(Int, Int)'}} +// Optional to Optional +optionalTuple = optionalTuple2 // expected-error {{cannot assign value of type '(Int64, Int)?' to type '(Int, Int)?'}} From bf7eea08da9af6b6e9ac0ee604641db59e748ba7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 24 May 2020 23:14:19 -0700 Subject: [PATCH 07/12] [Function builders] Infer function builder from a protocol requirement. Allow a protocol requirement for a function or property to declare a function builder. A witness to such a protocol requirement will infer that function builder when all of the following are two: * The witness does not explicitly adopt a function builder * The witness is declared within the same context that conforms to the protocol requirement (e.g., same extension or primary type declaration) * The witness's body does not contain a "return" statement (since those disable the function builder transform). --- include/swift/AST/DiagnosticsSema.def | 8 + lib/AST/Attr.cpp | 8 +- lib/AST/Decl.cpp | 8 - lib/Sema/BuilderTransform.cpp | 6 +- lib/Sema/TypeCheckAttr.cpp | 4 +- lib/Sema/TypeCheckRequestFunctions.cpp | 106 +++++++++- lib/Sema/TypeCheckStmt.cpp | 3 +- lib/Sema/TypeChecker.h | 3 + test/Constraints/function_builder_infer.swift | 186 ++++++++++++++++++ 9 files changed, 313 insertions(+), 19 deletions(-) create mode 100644 test/Constraints/function_builder_infer.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ac04f9a9526ef..78b05a8b47653 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5066,6 +5066,14 @@ NOTE(function_builder_remove_attr, none, "remove the attribute to explicitly disable the function builder", ()) NOTE(function_builder_remove_returns, none, "remove 'return' statements to apply the function builder", ()) +ERROR(function_builder_infer_ambig, none, + "ambiguous function builder inferred for %0: %1 or %2", + (DeclName, Type, Type)) +NOTE(function_builder_infer_add_return, none, + "add an explicit 'return' statement to not use a function builder", ()) +NOTE(function_builder_infer_pick_specific, none, + "apply function builder %0 (inferred from protocol %1)", + (Type, DeclName)) //------------------------------------------------------------------------------ // MARK: Tuple Shuffle Diagnostics diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 2b339f12fb75a..cf90377151b74 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -758,11 +758,13 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, case DAK_Custom: { if (!Options.IsForSwiftInterface) break; - // For Swift interface, we should only print function builder attribute - // on parameter decls. Printing the attribute elsewhere isn't ABI relevant. + // For Swift interface, we should print function builder attributes + // on parameter decls and on protocol requirements. + // Printing the attribute elsewhere isn't ABI relevant. if (auto *VD = dyn_cast(D)) { if (VD->getAttachedFunctionBuilder() == this) { - if (!isa(D)) + if (!isa(D) && + !(isa(D) && isa(D->getDeclContext()))) return false; } } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 71392bdb747cc..55e131accf4de 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6366,10 +6366,6 @@ void ParamDecl::setStoredProperty(VarDecl *var) { } Type ValueDecl::getFunctionBuilderType() const { - // Fast path: most declarations (especially parameters, which is where - // this is hottest) do not have any custom attributes at all. - if (!getAttrs().hasAttribute()) return Type(); - auto &ctx = getASTContext(); auto mutableThis = const_cast(this); return evaluateOrDefault(ctx.evaluator, @@ -6378,10 +6374,6 @@ Type ValueDecl::getFunctionBuilderType() const { } CustomAttr *ValueDecl::getAttachedFunctionBuilder() const { - // Fast path: most declarations (especially parameters, which is where - // this is hottest) do not have any custom attributes at all. - if (!getAttrs().hasAttribute()) return nullptr; - auto &ctx = getASTContext(); auto mutableThis = const_cast(this); return evaluateOrDefault(ctx.evaluator, diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index b2265af0885f2..b5b4b88c4c266 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -1401,10 +1401,6 @@ BraceStmt *swift::applyFunctionBuilderTransform( captured.first, captured.second))); } -/// Find the return statements in the given body, which block the application -/// of a function builder. -static std::vector findReturnStatements(AnyFunctionRef fn); - Optional TypeChecker::applyFunctionBuilderBodyTransform( FuncDecl *func, Type builderType) { // Pre-check the body: pre-check any expressions in it and look @@ -1708,7 +1704,7 @@ PreCheckFunctionBuilderRequest::evaluate(Evaluator &eval, return PreCheckFunctionBuilderApplication(fn, false).run(); } -std::vector findReturnStatements(AnyFunctionRef fn) { +std::vector TypeChecker::findReturnStatements(AnyFunctionRef fn) { PreCheckFunctionBuilderApplication precheck(fn, true); (void)precheck.run(); return precheck.getReturnStmts(); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index eb2d702d3407b..11638181661cf 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2971,9 +2971,11 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { // Module interfaces don't print bodies for all getters, so allow getters // that don't have a body if we're compiling a module interface. + // Within a protocol definition, there will never be a body. SourceFile *parent = storage->getDeclContext()->getParentSourceFile(); bool isInInterface = parent && parent->Kind == SourceFileKind::Interface; - if (!isInInterface && !getter->hasBody()) + if (!isInInterface && !getter->hasBody() && + !isa(storage->getDeclContext())) return true; return false; diff --git a/lib/Sema/TypeCheckRequestFunctions.cpp b/lib/Sema/TypeCheckRequestFunctions.cpp index fd789fc4df40d..ec58515f05568 100644 --- a/lib/Sema/TypeCheckRequestFunctions.cpp +++ b/lib/Sema/TypeCheckRequestFunctions.cpp @@ -18,6 +18,7 @@ #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/NameLookupRequests.h" +#include "swift/AST/ProtocolConformance.h" #include "swift/AST/TypeLoc.h" #include "swift/AST/Types.h" #include "swift/Subsystems.h" @@ -182,11 +183,114 @@ AttachedFunctionBuilderRequest::evaluate(Evaluator &evaluator, return nullptr; } +/// Attempt to infer the function builder type for a declaration. +static Type inferFunctionBuilderType(ValueDecl *decl) { + auto dc = decl->getDeclContext(); + if (!dc->isTypeContext()) + return Type(); + + auto funcDecl = dyn_cast(decl); + if (!funcDecl) + return Type(); + + // Only getters can have function builders. When we find one, look at + // the storage declaration for the purposes of witness matching. + auto lookupDecl = decl; + if (auto accessor = dyn_cast(funcDecl)) { + if (accessor->getAccessorKind() != AccessorKind::Get) + return Type(); + + lookupDecl = accessor->getStorage(); + } + + // Determine all of the conformances within the same context as + // this declaration. If this declaration is a witness to any + // requirement within one of those protocols that has a function builder + // attached, use that function builder type. + auto idc = cast(dc->getAsDecl()); + auto conformances = evaluateOrDefault( + dc->getASTContext().evaluator, + LookupAllConformancesInContextRequest{idc}, { }); + + // Find all of the potentially inferred function builder types. + struct Match { + ProtocolConformance *conformance; + ValueDecl *requirement; + Type functionBuilderType; + }; + SmallVector matches; + for (auto conformance : conformances) { + auto protocol = conformance->getProtocol(); + for (auto found : protocol->lookupDirect(lookupDecl->getName())) { + if (!isa(found->getDeclContext())) + continue; + + auto requirement = dyn_cast(found); + if (!requirement) + continue; + + Type functionBuilderType = requirement->getFunctionBuilderType(); + if (!functionBuilderType) + continue; + + auto witness = conformance->getWitnessDecl(requirement); + if (witness != lookupDecl) + continue; + + // Substitute into the function builder type. + auto subs = conformance->getSubstitutions(decl->getModuleContext()); + Type subFunctionBuilderType = functionBuilderType.subst(subs); + + matches.push_back({conformance, requirement, subFunctionBuilderType}); + } + } + + if (matches.size() == 0) + return Type(); + + // Check whether there are any return statements in the function's body. + // If there are, the function builder transform will be disabled, + // so don't infer a function builder. + if (!TypeChecker::findReturnStatements(funcDecl).empty()) + return Type(); + + // Determine whether there is more than one actual function builder type. + Type functionBuilderType = matches[0].functionBuilderType; + for (const auto &match : matches) { + // If the types were the same anyway, there's nothing to do. + Type otherFunctionBuilderType = match.functionBuilderType; + if (functionBuilderType->isEqual(otherFunctionBuilderType)) + continue; + + // We have at least two different function builder types. + // Diagnose the ambiguity and provide potential solutions. + decl->diagnose( + diag::function_builder_infer_ambig, lookupDecl->getName(), + functionBuilderType, otherFunctionBuilderType); + decl->diagnose(diag::function_builder_infer_add_return) + .fixItInsert(funcDecl->getBodySourceRange().End, "return <#expr#>\n"); + for (const auto &match : matches) { + decl->diagnose( + diag::function_builder_infer_pick_specific, + match.functionBuilderType, + match.conformance->getProtocol()->getName()) + .fixItInsert( + lookupDecl->getAttributeInsertionLoc(false), + "@" + match.functionBuilderType.getString() + " "); + } + + return Type(); + } + + return functionBuilderType; +} + Type FunctionBuilderTypeRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { // Look for a function-builder custom attribute. auto attr = decl->getAttachedFunctionBuilder(); - if (!attr) return Type(); + if (!attr) + return inferFunctionBuilderType(decl); // Resolve a type for the attribute. auto mutableAttr = const_cast(attr); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index a94e8c35edefc..2a0f34f76ef6e 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1862,7 +1862,8 @@ TypeCheckFunctionBodyUntilRequest::evaluate(Evaluator &evaluator, if (auto *func = dyn_cast(AFD)) { if (Type builderType = getFunctionBuilderType(func)) { if (auto optBody = - TypeChecker::applyFunctionBuilderBodyTransform(func, builderType)) { + TypeChecker::applyFunctionBuilderBodyTransform( + func, builderType)) { if (!*optBody) return true; diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 33961ce7894cd..09dd2dad42c42 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -561,6 +561,9 @@ bool typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD); Optional applyFunctionBuilderBodyTransform(FuncDecl *func, Type builderType); +/// Find the return statements within the body of the given function. +std::vector findReturnStatements(AnyFunctionRef fn); + bool typeCheckClosureBody(ClosureExpr *closure); bool typeCheckTapBody(TapExpr *expr, DeclContext *DC); diff --git a/test/Constraints/function_builder_infer.swift b/test/Constraints/function_builder_infer.swift new file mode 100644 index 0000000000000..7444cbb179110 --- /dev/null +++ b/test/Constraints/function_builder_infer.swift @@ -0,0 +1,186 @@ +// RUN: %target-typecheck-verify-swift -disable-availability-checking + +enum Either { + case first(T) + case second(U) +} + +@_functionBuilder +struct TupleBuilder { + static func buildBlock() -> () { + return () + } + + static func buildBlock(_ t1: T1) -> (T1) { + return (t1) + } + + static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { + return (t1, t2) + } + + static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3) + -> (T1, T2, T3) { + return (t1, t2, t3) + } + + static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4) + -> (T1, T2, T3, T4) { + return (t1, t2, t3, t4) + } + + static func buildBlock( + _ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5 + ) -> (T1, T2, T3, T4, T5) { + return (t1, t2, t3, t4, t5) + } + + static func buildDo(_ value: T) -> T { return value } + static func buildIf(_ value: T?) -> T? { return value } + + static func buildEither(first value: T) -> Either { + return .first(value) + } + static func buildEither(second value: U) -> Either { + return .second(value) + } +} + +protocol Tupled { + associatedtype TupleType + + @TupleBuilder var tuple: TupleType { get } +} + +struct TupleMe: Tupled { + var condition: Bool + + // Okay: applies the function builder @TupleBuilder. + var tuple: some Any { + "hello" + if condition { + "nested" + } + 3.14159 + "world" + } +} + +// Witness is separated from the context declaring conformance, so don't infer +// the function builder. +struct DoNotTupleMe { + var condition: Bool + + var tuple: some Any { // expected-error{{function declares an opaque return type, but has no return statements in its body from which to infer an underlying type}} + "hello" // expected-warning{{string literal is unused}} + "world" // expected-warning{{string literal is unused}} + } +} + +extension DoNotTupleMe: Tupled { } + +@_functionBuilder +struct OtherTupleBuilder { + static func buildBlock() -> () { + return () + } + + static func buildBlock(_ t1: T1) -> (T1) { + return (t1) + } + + static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { + return (t1, t2) + } + + static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3) + -> (T1, T2, T3) { + return (t1, t2, t3) + } + + static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4) + -> (T1, T2, T3, T4) { + return (t1, t2, t3, t4) + } + + static func buildBlock( + _ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5 + ) -> (T1, T2, T3, T4, T5) { + return (t1, t2, t3, t4, t5) + } + + static func buildDo(_ value: T) -> T { return value } + static func buildIf(_ value: T?) -> T? { return value } + + static func buildEither(first value: T) -> Either { + return .first(value) + } + static func buildEither(second value: U) -> Either { + return .second(value) + } +} + +protocol Tupled2 { + associatedtype TupleType + + @TupleBuilder var tuple: TupleType { get } +} + +struct TupleMe2: Tupled, Tupled2 { + var condition: Bool + + // Okay: applies the function builder @TupleBuilder, even though it satisfies + // two requirements. (They have the same function builder) + var tuple: some Any { + "hello" + if condition { + "nested" + } + 3.14159 + "world" + } +} + +protocol OtherTupled { + associatedtype OtherTupleType + + @OtherTupleBuilder var tuple: OtherTupleType { get } +} + +struct AmbigTupleMe: Tupled, OtherTupled { + var condition: Bool + + // Ambiguous + internal + var tuple: Void { // expected-error{{ambiguous function builder inferred for 'tuple': 'TupleBuilder' or 'OtherTupleBuilder'}} + // expected-note@-1{{add an explicit 'return' statement to not use a function builder}}{{3-3=return <#expr#>\n}} + // expected-note@-2{{apply function builder 'TupleBuilder' (inferred from protocol 'Tupled')}}{{3-3=@TupleBuilder }} + // expected-note@-3{{apply function builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}}{{3-3=@OtherTupleBuilder }} + "hello" // expected-warning{{string literal is unused}} + "world" // expected-warning{{string literal is unused}} + } +} + +// Separating the conformances resolves the ambiguity. +struct TupleMeResolvedSeparate: Tupled { + var condition: Bool + + var tuple: some Any { + "hello" + if condition { + "nested" + } + 3.14159 + "world" + } +} + +extension TupleMeResolvedSeparate: OtherTupled { } + +struct TupleMeResolvedExplicit: Tupled, OtherTupled { + var condition: Bool + + var tuple: some Any { + return "hello" + } +} From c5a9575ebc6799a065a61f8ecf58a1fe60abeb67 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 25 May 2020 13:30:46 -0700 Subject: [PATCH 08/12] Cache LookupAllConformancesInContextRequest. Now that this request will be triggered more often, due to inference of function builders, cache the result. --- include/swift/AST/ASTTypeIDZone.def | 1 + include/swift/AST/ASTTypeIDs.h | 1 + include/swift/AST/TypeCheckRequests.h | 6 ++++-- include/swift/Basic/SimpleDisplay.h | 14 ++++++++++++++ lib/Sema/TypeCheckProtocol.cpp | 5 +++-- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index 103b382e9ba50..5c1eaa91fe286 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -57,6 +57,7 @@ SWIFT_TYPEID_NAMED(PatternBindingEntry *, PatternBindingEntry) SWIFT_TYPEID_NAMED(PostfixOperatorDecl *, PostfixOperatorDecl) SWIFT_TYPEID_NAMED(PrecedenceGroupDecl *, PrecedenceGroupDecl) SWIFT_TYPEID_NAMED(PrefixOperatorDecl *, PrefixOperatorDecl) +SWIFT_TYPEID_NAMED(ProtocolConformance *, ProtocolConformance) SWIFT_TYPEID_NAMED(ProtocolDecl *, ProtocolDecl) SWIFT_TYPEID_NAMED(SourceFile *, SourceFile) SWIFT_TYPEID_NAMED(TypeAliasDecl *, TypeAliasDecl) diff --git a/include/swift/AST/ASTTypeIDs.h b/include/swift/AST/ASTTypeIDs.h index c4bebdfade92b..9359f95427d0e 100644 --- a/include/swift/AST/ASTTypeIDs.h +++ b/include/swift/AST/ASTTypeIDs.h @@ -52,6 +52,7 @@ struct PropertyWrapperTypeInfo; enum class CtorInitializerKind; struct PropertyWrapperLValueness; struct PropertyWrapperMutability; +class ProtocolConformance; class ProtocolDecl; class Requirement; enum class ResilienceExpansion : unsigned; diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 200663d00cb80..356d5c7e1c2b6 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2239,7 +2239,7 @@ class ClosureHasExplicitResultRequest bool isCached() const { return true; } }; -using ProtocolConformanceLookupResult = SmallVector; +using ProtocolConformanceLookupResult = std::vector; void simple_display(llvm::raw_ostream &out, ConformanceLookupKind kind); /// Lookup and expand all conformances in the given context. @@ -2261,7 +2261,7 @@ class LookupAllConformancesInContextRequest : public SimpleRequest { public: @@ -2275,6 +2275,8 @@ class LookupAllConformancesInContextRequest evaluate(Evaluator &evaluator, const IterableDeclContext *IDC) const; public: + bool isCached() const { return true; } + // Incremental dependencies evaluator::DependencySource readDependencySource(const evaluator::DependencyRecorder &eval) const; diff --git a/include/swift/Basic/SimpleDisplay.h b/include/swift/Basic/SimpleDisplay.h index 3a553359091aa..3a31da14654ac 100644 --- a/include/swift/Basic/SimpleDisplay.h +++ b/include/swift/Basic/SimpleDisplay.h @@ -136,6 +136,20 @@ namespace swift { out << "}"; } + template + void simple_display(llvm::raw_ostream &out, + const std::vector &vec) { + out << "{"; + bool first = true; + for (const T &value : vec) { + if (first) first = false; + else out << ", "; + + simple_display(out, value); + } + out << "}"; + } + template void simple_display(llvm::raw_ostream &out, const llvm::PointerUnion &ptrUnion) { diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 9f7357de1afd8..2bf213e44986e 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -5038,10 +5038,11 @@ diagnoseMissingAppendInterpolationMethod(NominalTypeDecl *typeDecl) { } } -SmallVector +std::vector LookupAllConformancesInContextRequest::evaluate( Evaluator &eval, const IterableDeclContext *IDC) const { - return IDC->getLocalConformances(ConformanceLookupKind::All); + auto result = IDC->getLocalConformances(ConformanceLookupKind::All); + return std::vector(result.begin(), result.end()); } void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) { From 54c1267217c95204eb63572d6f41222e22562143 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 25 May 2020 13:36:46 -0700 Subject: [PATCH 09/12] [Function builders] Short-circuit function builder inference earlier. Eliminates both circular references and unwanted dependencies. --- lib/Sema/TypeCheckRequestFunctions.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/Sema/TypeCheckRequestFunctions.cpp b/lib/Sema/TypeCheckRequestFunctions.cpp index ec58515f05568..bf26315290381 100644 --- a/lib/Sema/TypeCheckRequestFunctions.cpp +++ b/lib/Sema/TypeCheckRequestFunctions.cpp @@ -186,11 +186,18 @@ AttachedFunctionBuilderRequest::evaluate(Evaluator &evaluator, /// Attempt to infer the function builder type for a declaration. static Type inferFunctionBuilderType(ValueDecl *decl) { auto dc = decl->getDeclContext(); - if (!dc->isTypeContext()) + if (!dc->isTypeContext() || isa(dc)) return Type(); auto funcDecl = dyn_cast(decl); - if (!funcDecl) + if (!funcDecl || !funcDecl->hasBody() || + !decl->getDeclContext()->getParentSourceFile()) + return Type(); + + // Check whether there are any return statements in the function's body. + // If there are, the function builder transform will be disabled, + // so don't infer a function builder. + if (!TypeChecker::findReturnStatements(funcDecl).empty()) return Type(); // Only getters can have function builders. When we find one, look at @@ -248,12 +255,6 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { if (matches.size() == 0) return Type(); - // Check whether there are any return statements in the function's body. - // If there are, the function builder transform will be disabled, - // so don't infer a function builder. - if (!TypeChecker::findReturnStatements(funcDecl).empty()) - return Type(); - // Determine whether there is more than one actual function builder type. Type functionBuilderType = matches[0].functionBuilderType; for (const auto &match : matches) { From 5d9d4f9434d58446c202b62bcc1d008b04bc9211 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 25 May 2020 13:39:17 -0700 Subject: [PATCH 10/12] [Tests] Add newly-introduced dependencies to the incremental verifier. --- .../Verifier/single-file-private/AnyObject.swift | 8 ++++++++ test/Incremental/Verifier/single-file/AnyObject.swift | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/test/Incremental/Verifier/single-file-private/AnyObject.swift b/test/Incremental/Verifier/single-file-private/AnyObject.swift index d7cac96fdffa1..e9c9b723dab33 100644 --- a/test/Incremental/Verifier/single-file-private/AnyObject.swift +++ b/test/Incremental/Verifier/single-file-private/AnyObject.swift @@ -60,3 +60,11 @@ func lookupOnAnyObject(object: AnyObject) { // expected-provides {{lookupOnAnyOb _ = object.someMember // expected-private-dynamic-member {{someMember}} object.someMethod() // expected-private-dynamic-member {{someMethod}} } + +// expected-private-member {{Swift.Hashable.someMethod}} +// expected-private-member {{Foundation._KeyValueCodingAndObserving.someMethod}} +// expected-private-member {{Foundation._KeyValueCodingAndObservingPublishing.someMethod}} +// expected-private-member {{Swift.Equatable.someMethod}} +// expected-private-member {{Swift.CVarArg.someMethod}} +// expected-private-member {{Swift.CustomStringConvertible.someMethod}} +// expected-private-member {{Swift.CustomDebugStringConvertible.someMethod}} diff --git a/test/Incremental/Verifier/single-file/AnyObject.swift b/test/Incremental/Verifier/single-file/AnyObject.swift index eccf877fbd05e..cc1509f866cd1 100644 --- a/test/Incremental/Verifier/single-file/AnyObject.swift +++ b/test/Incremental/Verifier/single-file/AnyObject.swift @@ -60,3 +60,10 @@ func lookupOnAnyObject(object: AnyObject) { // expected-provides {{lookupOnAnyOb _ = object.someMember // expected-private-dynamic-member {{someMember}} object.someMethod() // expected-private-dynamic-member {{someMethod}} } +// expected-private-member {{Swift.Hashable.someMethod}} +// expected-private-member {{Foundation._KeyValueCodingAndObserving.someMethod}} +// expected-private-member {{Foundation._KeyValueCodingAndObservingPublishing.someMethod}} +// expected-private-member {{Swift.Equatable.someMethod}} +// expected-private-member {{Swift.CVarArg.someMethod}} +// expected-private-member {{Swift.CustomStringConvertible.someMethod}} +// expected-private-member {{Swift.CustomDebugStringConvertible.someMethod}} From e43cd9d78457c06fccd5a54d6e48e94a6114f839 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Mon, 25 May 2020 20:18:03 -0300 Subject: [PATCH 11/12] [CSFix] Ajusting contextual type purpose for subscript destination for assign expr in getStructuralTypeContext --- lib/Sema/CSDiagnostics.cpp | 9 +-------- lib/Sema/CSFix.cpp | 4 +++- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 0bdb4e75e443e..6900f5bd38989 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -2989,14 +2989,7 @@ bool TupleContextualFailure::diagnoseAsError() { diagnostic = diag::tuple_types_not_convertible_nelts; else if ((purpose == CTP_Initialization) && !getContextualType(getAnchor())) diagnostic = diag::tuple_types_not_convertible; - else if (purpose == CTP_AssignSource) { - auto assignExpr = castToExpr(getAnchor()); - if (isa(assignExpr->getDest())) { - diagnostic = *getDiagnosticFor(CTP_SubscriptAssignSource, getToType()); - } else { - diagnostic = *getDiagnosticFor(purpose, getToType()); - } - } else if (auto diag = getDiagnosticFor(purpose, getToType())) + else if (auto diag = getDiagnosticFor(purpose, getToType())) diagnostic = *diag; else return false; diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index fb0950e2e4e89..914e8cff50ce6 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -287,7 +287,9 @@ getStructuralTypeContext(const Solution &solution, ConstraintLocator *locator) { solution.getType(coerceExpr->getSubExpr()), solution.getType(coerceExpr)); } else if (auto *assignExpr = getAsExpr(locator->getAnchor())) { - return std::make_tuple(CTP_AssignSource, + auto CTP = isa(assignExpr->getDest()) ? CTP_SubscriptAssignSource + : CTP_AssignSource; + return std::make_tuple(CTP, solution.getType(assignExpr->getSrc()), solution.getType(assignExpr->getDest())->getRValueType()); } else if (auto *call = getAsExpr(locator->getAnchor())) { From 8cec6b555d765379dd585e2ccb7eb079e74e98fe Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 25 May 2020 23:42:45 -0700 Subject: [PATCH 12/12] [Function builders] Add tests for function builders on protocol requirements --- test/ModuleInterface/function_builders.swift | 7 ++++ test/Serialization/function_builders.swift | 36 ++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 test/Serialization/function_builders.swift diff --git a/test/ModuleInterface/function_builders.swift b/test/ModuleInterface/function_builders.swift index 1dc7d3e7d40dc..e471241774eda 100644 --- a/test/ModuleInterface/function_builders.swift +++ b/test/ModuleInterface/function_builders.swift @@ -47,3 +47,10 @@ public struct UsesBuilderProperty { // CHECK: public func myFunc(@FunctionBuilders.TupleBuilder fn: () -> ()) public func myFunc(@TupleBuilder fn: () -> ()) {} } + +public protocol ProtocolWithBuilderProperty { + associatedtype Assoc + + // CHECK: @FunctionBuilders.TupleBuilder var myVar: Self.Assoc { get } + @TupleBuilder var myVar: Assoc { get } +} diff --git a/test/Serialization/function_builders.swift b/test/Serialization/function_builders.swift new file mode 100644 index 0000000000000..1f866ff7538e0 --- /dev/null +++ b/test/Serialization/function_builders.swift @@ -0,0 +1,36 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t %s +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -skip-deinit=false -module-to-print=function_builders -I %t -source-filename=%s | %FileCheck %s + +@_functionBuilder +public struct TupleBuilder { + public static func buildBlock(_ t1: T1, _ t2: T2) -> (T1, T2) { + return (t1, t2) + } + + public static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3) + -> (T1, T2, T3) { + return (t1, t2, t3) + } + + public static func buildBlock(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4) + -> (T1, T2, T3, T4) { + return (t1, t2, t3, t4) + } + + public static func buildBlock( + _ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5 + ) -> (T1, T2, T3, T4, T5) { + return (t1, t2, t3, t4, t5) + } + + public static func buildDo(_ value: T) -> T { return value } + public static func buildIf(_ value: T?) -> T? { return value } +} + +public protocol ProtocolWithBuilderProperty { + associatedtype Assoc + + // CHECK: @TupleBuilder var myVar: Self.Assoc { get } + @TupleBuilder var myVar: Assoc { get } +}